Chromium在Browser进程中为网页创建了一个Frame Tree之后,会将网页的URL发送给Render进程进行载入。Render进程接收到网页URL载入请求之后,会做一些必要的初始化工作,然后请求Browser进程下载网页的内容。Browser进程一边下载网页内容。一边又通过共享内存将网页内容传递给Render进程解析。也就是创建DOM Tree。本文接下来就分析网页URL的载入过程。

老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注!

《Android系统源码情景分析》一书正在进击的程序猿网(http://0xcc0xcd.com)中连载。点击进入!

Render进程之所以要请求Browser进程下载网页的内容,是由于Render进程没有网络訪问权限。

出于安全考虑。Chromium将Render进程启动在一个受限环境中,使得Render进程没有网络訪问权限。那为什么不是Browser进程主动下载好网页内容再交给Render进程解析呢?

这是由于Render进程是通过WebKit载入网页URL的,WebKit不关心自己所在的进程是否有网络訪问权限,它通过特定的接口訪问网络。

这个特定接口由WebKit的使用者。也就是Render进程中的Content模块实现。

Content模块在实现这个接口的时候,会通过IPC请求Browser进程下载网络的内容。这样的设计方式使得WebKit能够灵活地使用:既能够在有网络訪问权限的进程中使用,也能够在没有网络訪问权限的进程中使用。而且使用方式是统一的。

从前面Chromium Frame Tree创建过程分析一文能够知道,Browser进程中为要载入的网页创建了一个Frame Tree之后,会向Render进程发送一个类型为FrameMsg_Navigate的IPC消息。Render进程接收到这个IPC消息之后。处理流程如图1所看到的:

图1 网页URL载入过程

Render进程运行了一些初始化工作之后,就向Browser进程发送一个类型为ResourceHostMsg_RequestResource的IPC消息。Browser进程收到这个IPC消息之后。就会通过HTTP协议请求Webserver将网页的内容返回来。

请求得到响应后。Browser进程就会创建一块共享内存,而且通过一个类型为ResourceMsg_SetDataBuffer的IPC消息将这块共享内存传递给Render进程的。

以后每当下载到新的网页内容,Browser进程就会将它们写入到前面创建的共享内存中去,而且发送Render进程发送一个类型为ResourceMsg_DataReceived的IPC消息。Render进程接收到这个IPC消息之后,就会从共享内存中读出Browser进程写入的内容。而且进行解析。也就是创建一个DOM Tree。这个过程一直持续到网页内容下载完毕为止。

接下来,我们就从Render进程接收类型为FrameMsg_Navigate的IPC消息開始分析网页URL的载入过程。

Render进程是通过RenderFrameImpl类的成员函数OnMessageReceived接收类型为FrameMsg_Navigate的IPC消息的。例如以下所看到的:

bool RenderFrameImpl::OnMessageReceived(const IPC::Message& msg) {
...... bool handled = true;
IPC_BEGIN_MESSAGE_MAP(RenderFrameImpl, msg)
IPC_MESSAGE_HANDLER(FrameMsg_Navigate, OnNavigate)
......
IPC_END_MESSAGE_MAP() return handled;
}

这个函数定义在文件external/chromium_org/content/renderer/render_frame_impl.cc中。

RenderFrameImpl类的成员函数OnMessageReceived将类型为FrameMsg_Navigate的IPC消息分发给另外一个成员函数OnNavigate处理,后者的实现例如以下所看到的:

void RenderFrameImpl::OnNavigate(const FrameMsg_Navigate_Params& params) {
...... bool is_reload = RenderViewImpl::IsReload(params);
...... WebFrame* frame = frame_;
...... if (is_reload) {
......
} else if (params.page_state.IsValid()) {
......
} else if (!params.base_url_for_data_url.is_empty()) {
......
} else {
// Navigate to the given URL.
WebURLRequest request(params.url);
...... frame->loadRequest(request); ......
} ......
}

这个函数定义在文件external/chromium_org/content/renderer/render_frame_impl.cc中。 

从前面Chromium Frame Tree创建过程分析一文能够知道,RenderFrameImpl类的成员变量frame_指向的是一个WebLocalFrameImpl对象。假设当前正在处理的RenderFrameImpl对象还没有载入过URL,而且当前要载入的URL不为空,RenderFrameImpl类的成员函数OnNavigate会调用成员变量frame_指向的WebLocalFrameImpl对象的成员函数loadRequest载入指定的URL。

WebLocalFrameImpl类的成员函数loadRequest的实现例如以下所看到的:

void WebLocalFrameImpl::loadRequest(const WebURLRequest& request)
{
......
const ResourceRequest& resourceRequest = request.toResourceRequest(); if (resourceRequest.url().protocolIs("javascript")) {
loadJavaScriptURL(resourceRequest.url());
return;
} frame()->loader().load(FrameLoadRequest(0, resourceRequest));
}

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp中。

假设參数request描写叙述的URL指定的协议是"javascript",那么表示要载入的是一段JavaScript。这时候WebLocalFrameImpl类的成员函数loadRequest会调用另外一个成员函数loadJavaScriptURL载入这段JavaScript。

在其他情况下,WebLocalFrameImpl类的成员函数loadRequest首先调用成员函数frame获得成员变量m_frame描写叙述的一个LocalFrame对象,接着又调用这个LocalFrame对象的成员函数loader获得其成员变量m_loader描写叙述的一个FrameLoader对象。有了这个FrameLoader对象之后。就调用它的成员函数load载入參数request描写叙述的URL。

WebLocalFrameImpl类的成员变量m_frame描写叙述的LocalFrame对象和LocalFrame类的成员变量m_loader描写叙述的FrameLoader对象的创建过程,能够參考前面Chromium Frame Tree创建过程分析一文。接下来我们继续分析FrameLoader类的成员函数load的实现,例如以下所看到的:

void FrameLoader::load(const FrameLoadRequest& passedRequest)
{
...... FrameLoadRequest request(passedRequest);
...... FrameLoadType newLoadType = determineFrameLoadType(request);
NavigationAction action(request.resourceRequest(), newLoadType, request.formState(), request.triggeringEvent());
...... loadWithNavigationAction(action, newLoadType, request.formState(), request.substituteData(), request.clientRedirect()); ......
}

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/loader/FrameLoader.cpp中。

FrameLoader类的成员函数load主要是调用另外一个成员函数loadWithNavigationAction载入參数passedRequest描写叙述的URL。

FrameLoader类的成员函数loadWithNavigationAction的实现例如以下所看到的:

void FrameLoader::loadWithNavigationAction(const NavigationAction& action, FrameLoadType type, PassRefPtrWillBeRawPtr<FormState> formState, const SubstituteData& substituteData, ClientRedirectPolicy clientRedirect, const AtomicString& overrideEncoding)
{
...... const ResourceRequest& request = action.resourceRequest();
...... m_policyDocumentLoader = client()->createDocumentLoader(m_frame, request, substituteData.isValid() ? substituteData : defaultSubstituteDataForURL(request.url()));
...... m_provisionalDocumentLoader = m_policyDocumentLoader.release();
...... m_provisionalDocumentLoader->startLoadingMainResource();
}

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/loader/FrameLoader.cpp中。

FrameLoader类的成员函数loadWithNavigationAction首先调用成员函数client获得一个FrameLoaderClientImpl对象。接着再调用这个FrameLoaderClientImpl对象的成员函数createDocumentLoader为參数action描写叙述的URL创建了一个WebDataSourceImpl对象,而且保存在成员变量m_policyDocumentLoader中。

关于FrameLoader类的成员函数client和FrameLoaderClientImpl类的成员函数createDocumentLoader的实现。能够參考前面Chromium Frame Tree创建过程分析一文。

FrameLoader类的成员函数loadWithNavigationAction接下来又将成员变量m_policyDocumentLoader描写叙述的WebDataSourceImpl对象转移到另外一个成员变量m_provisionalDocumentLoader中。最后调用这个WebDataSourceImpl对象的成员函数startLoadingMainResource载入參数action描写叙述的URL。

WebDataSourceImpl类的成员函数startLoadingMainResource是从父类DocumentLoader继承下来的,它的实现例如以下所看到的:

void DocumentLoader::startLoadingMainResource()
{
...... FetchRequest cachedResourceRequest(request, FetchInitiatorTypeNames::document, mainResourceLoadOptions);
m_mainResource = m_fetcher->fetchMainResource(cachedResourceRequest, m_substituteData);
...... m_mainResource->addClient(this); ......
}

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/loader/DocumentLoader.cpp中。

从前面Chromium Frame Tree创建过程分析一文能够知道,DocumentLoader类的成员变量m_fetcher描写叙述的是一个ResourceFetcher对象,DocumentLoader类的成员函数startLoadingMainResource调用这个ResourceFetcher对象的成员函数fetchMainResource请求载入本地变量cachedResourceRequest描写叙述的资源。这个资源描写叙述的即为上一步指定要载入的URL。

ResourceFetcher类的成员函数fetchMainResource运行结束后。会返回一个RawResource对象。这个RawResource对象保存在WebDataSourceImpl类的成员变量m_mainResource中。这个RawResource对象描写叙述的是一个异步载入的资源。DocumentLoader类的成员startLoadingMainResource调用它的成员函数addClient将当前正在处理的DocumentLoader对象加入到它的内部去,用来获得异步载入的资源数据,也就是本地变量cachedResourceRequest描写叙述的URL相应的网页内容。

RawResource类的成员函数addClient是从父类Resource继承下来的,它的实现例如以下所看到的:

void Resource::addClient(ResourceClient* client)
{
if (addClientToSet(client))
didAddClient(client);
}

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/fetch/Resource.cpp中。

Resource类的成员函数addClient调用另外一个成员函数addClientToSet将參数client描写叙述的一个DocumentLoader对象保存在内部,例如以下所看到的:

bool Resource::addClientToSet(ResourceClient* client)
{
...... m_clients.add(client);
return true;
}

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/fetch/Resource.cpp中。

Resource类的成员函数addClientToSet将參数client描写叙述的一个DocumentLoader保存在成员变量m_clients描写叙述的一个Hash Set中,以便当前正在处理的Resource对象描写叙述的网页内容从Webserver下载回来的时候。能够交给它处理。

接下来我们继续分析WebDataSourceImpl类的成员函数startLoadingMainResource调用成员变量m_fetcher描写叙述的ResourceFetcher对象的成员函数fetchMainResource载入本地变量cachedResourceRequest描写叙述的URL的过程,例如以下所看到的:

ResourcePtr<RawResource> ResourceFetcher::fetchMainResource(FetchRequest& request, const SubstituteData& substituteData)
{
......
return toRawResource(requestResource(Resource::MainResource, request));
}

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp中。

ResourceFetcher类的成员函数fetchMainResource调用另外一个成员函数requestResource载入參数request描写叙述的URL。

ResourceFetcher类的成员函数requestResource会返回一个RawResource对象给调用者,即ResourceFetcher类的成员函数fetchMainResource。

后者又会将这个RawResource对象返回给它的调用者。

ResourceFetcher类的成员函数requestResource的实现例如以下所看到的:

ResourcePtr<Resource> ResourceFetcher::requestResource(Resource::Type type, FetchRequest& request)
{
...... KURL url = request.resourceRequest().url();
...... const RevalidationPolicy policy = determineRevalidationPolicy(type, request.mutableResourceRequest(), request.forPreload(), resource.get(), request.defer(), request.options());
switch (policy) {
......
case Load:
resource = createResourceForLoading(type, request, request.charset());
break;
.....
} ...... if (resourceNeedsLoad(resource.get(), request, policy)) {
...... if (!m_documentLoader || !m_documentLoader->scheduleArchiveLoad(resource.get(), request.resourceRequest()))
resource->load(this, request.options()); ......
} ...... return resource;
}

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp中。

ResourceFetcher类的成员函数requestResource首先调用成员函数createResourceForLoading为參数request描写叙述的URL创建一个RawResource对象。例如以下所看到的:

ResourcePtr<Resource> ResourceFetcher::createResourceForLoading(Resource::Type type, FetchRequest& request, const String& charset)
{
...... ResourcePtr<Resource> resource = createResource(type, request.resourceRequest(), charset); ......
return resource;
}

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp中。

ResourceFetcher类的成员函数createResourceForLoading调用函数createResource依据參数type和request创建一个RawResource对象,例如以下所看到的:

static Resource* createResource(Resource::Type type, const ResourceRequest& request, const String& charset)
{
switch (type) {
......
case Resource::MainResource:
case Resource::Raw:
case Resource::TextTrack:
case Resource::Media:
return new RawResource(request, type);
......
} ......
return 0;
}

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp中。

从前面的调用过程能够知道,參数type的值等于Resource::MainResource,因此函数createResource创建的是一个RawResource对象。

回到ResourceFetcher类的成员函数requestResource中。它调用成员函数createResourceForLoading为參数request描写叙述的URL创建了一个RawResource对象之后。接下来又调用成员函数resourceNeedsLoad推断该URL是否须要进行载入。假设须要进行载入,那么ResourceFetcher类的成员函数requestResource又会调用成员变量m_documentLoader描写叙述的一个DocumentLoader对象的成员函数scheduleArchiveLoad推断要载入的URL描写叙述的是否是一个存档文件。假设不是,那么就会调用前面创建的RawResource对象的成员函数load从Webserver下载相应的网页内容。

我们假设request描写叙述的URL须要进行载入,而且不是一个存档文件。因此接下来我们继续分析RawResource类的成员函数load的实现。RawResource类的成员函数load是从父类Resource继承下来的。它的实现例如以下所看到的:

void Resource::load(ResourceFetcher* fetcher, const ResourceLoaderOptions& options)
{
...... ResourceRequest request(m_resourceRequest);
...... m_loader = ResourceLoader::create(fetcher, this, request, options);
m_loader->start();
}

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/fetch/Resource.cpp中。

Resource类的成员变量m_resourceRequest描写叙述的是要载入的URL,Resource类的成员函数load首先调用ResourceLoader类的静态成员函数create为其创建一个ResourceLoader对象,例如以下所看到的:

PassRefPtr<ResourceLoader> ResourceLoader::create(ResourceLoaderHost* host, Resource* resource, const ResourceRequest& request, const ResourceLoaderOptions& options)
{
RefPtr<ResourceLoader> loader(adoptRef(new ResourceLoader(host, resource, options)));
loader->init(request);
return loader.release();
}

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/fetch/ResourceLoader.cpp中。

从这里能够看到,ResourceLoader类的静态成员函数create创建的是一个ResourceLoader对象。这个ResourceLoader对象经过初始化之后,会返回给调用者。

回到Resource类的成员函数load中,它为要载入的URL创建了一个ResourceLoader对象之后,会调用这个ResourceLoader对象的成员函数start開始载入要载入的URL,例如以下所看到的:

void ResourceLoader::start()
{
...... m_loader = adoptPtr(blink::Platform::current()->createURLLoader());
......
blink::WrappedResourceRequest wrappedRequest(m_request);
m_loader->loadAsynchronously(wrappedRequest, this);
}

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/fetch/ResourceLoader.cpp中。

ResourceLoader类的成员函数start首先调用由Chromium的Content模块实现的一个blink::Platform接口的成员函数createURLLoader创建一个WebURLLoaderImpl对象,接着再调用这个WebURLLoaderImpl对象的成员函数loadAsynchronously对象成员变量m_request描写叙述的URL进行异步载入。

Chromium的Content模块的BlinkPlatformImpl类实现了blink::Platform接口,它的成员函数createURLLoader的实现例如以下所看到的:

WebURLLoader* BlinkPlatformImpl::createURLLoader() {
return new WebURLLoaderImpl;
}

这个函数定义在文件external/chromium_org/content/child/blink_platform_impl.cc中。

从这里能够看到。BlinkPlatformImpl类的成员函数createURLLoader创建的是一个WebURLLoaderImpl对象。这个WebURLLoaderImpl对象会返回给调用者。

接下来我们继续分析WebURLLoaderImpl类的成员函数loadAsynchronously异步载入一个URL的过程。例如以下所看到的:

void WebURLLoaderImpl::loadAsynchronously(const WebURLRequest& request,
WebURLLoaderClient* client) {
...... context_->set_client(client);
context_->Start(request, NULL);
}

这个函数定义在文件external/chromium_org/content/child/web_url_loader_impl.cc中。

从前面的调用过程能够知道。參数client描写叙述的是一个ResourceLoader对象。

这个ResourceLoader对象会保存在WebURLLoaderImpl类的成员变量content_描写叙述的一个WebURLLoaderImpl::Context对象的内部。这是通过调用WebURLLoaderImpl::Context类的成员函数set_client实现的。例如以下所看到的:

class WebURLLoaderImpl::Context : public base::RefCounted<Context>,
public RequestPeer {
public:
...... void set_client(WebURLLoaderClient* client) { client_ = client; } private:
...... WebURLLoaderClient* client_; ......
};

这个函数定义在文件external/chromium_org/content/child/web_url_loader_impl.cc中。

WebURLLoaderImpl::Context类的成员函数set_client将參数client描写叙述的ResourceLoader对象保存在成员变量client_中。

回到WebURLLoaderImpl类的成员函数loadAsynchronously中,它接下来会继续调用成员变量content_描写叙述的一个WebURLLoaderImpl::Context对象的成员函数Start载入參数request描写叙述的URL,例如以下所看到的:

void WebURLLoaderImpl::Context::Start(const WebURLRequest& request,
SyncLoadResponse* sync_load_response) {
...... GURL url = request.url();
...... RequestInfo request_info;
......
request_info.url = url;
......
bridge_.reset(ChildThread::current()->resource_dispatcher()->CreateBridge(
request_info)); ...... if (bridge_->Start(this)) {
AddRef(); // Balanced in OnCompletedRequest
} else {
bridge_.reset();
}
}

这个函数定义在文件external/chromium_org/content/child/web_url_loader_impl.cc中。

WebURLLoaderImpl::Context类的成员函数Start首先调用当前Render进程的一个ChildThread单例的成员函数resource_dispatcher获得一个ResourceDispatcher对象。例如以下所看到的:

class CONTENT_EXPORT ChildThread
: public IPC::Listener,
public IPC::Sender,
public NON_EXPORTED_BASE(mojo::ServiceProvider) {
public:
...... ResourceDispatcher* resource_dispatcher() const {
return resource_dispatcher_.get();
} ...... private:
...... // Handles resource loads for this process.
scoped_ptr<ResourceDispatcher> resource_dispatcher_; ......
};

这个函数定义在文件external/chromium_org/content/child/child_thread.h中。

ChildThread类的成员函数resource_dispatcher返回的是成员变量resource_dispatcher_描写叙述的一个ResourceDispatcher对象。

回到WebURLLoaderImpl::Context类的成员函数Start中,它获得了一个ResourceDispatcher对象之后,接着调用这个ResourceDispatcher对象的成员函数CreateBridge创建一个IPCResourceLoaderBridge对象,例如以下所看到的:

ResourceLoaderBridge* ResourceDispatcher::CreateBridge(
const RequestInfo& request_info) {
return new IPCResourceLoaderBridge(this, request_info);
}

这个函数定义在文件external/chromium_org/content/child/resource_dispatcher.cc中。

从这里能够看到,ResourceDispatcher类的成员函数CreateBridge创建的是一个IPCResourceLoaderBridge对象,而且会将这个IPCResourceLoaderBridge对象返回给调用者。

回到WebURLLoaderImpl::Context类的成员函数Start中,它获得了一个IPCResourceLoaderBridge对象之后。接着调用这个IPCResourceLoaderBridge对象的成员函数Start载入參数request描写叙述的URL。例如以下所看到的:

bool IPCResourceLoaderBridge::Start(RequestPeer* peer) {
...... // generate the request ID, and append it to the message
request_id_ = dispatcher_->AddPendingRequest(peer,
request_.resource_type,
request_.origin_pid,
frame_origin_,
request_.url,
request_.download_to_file); return dispatcher_->message_sender()->Send(
new ResourceHostMsg_RequestResource(routing_id_, request_id_, request_));
}

这个函数定义在文件external/chromium_org/content/child/resource_dispatcher.cc中。

IPCResourceLoaderBridge类的成员变量dispatcher_描写叙述的是一个ResourceDispatcher对象,IPCResourceLoaderBridge类的成员函数Start首先调用这个ResourceDispatcher对象的成员函数AddPendingRequest将參数peer描写叙述的一个WebURLLoaderImpl::Context对象保存在内部。例如以下所看到的:

int ResourceDispatcher::AddPendingRequest(RequestPeer* callback,
ResourceType::Type resource_type,
int origin_pid,
const GURL& frame_origin,
const GURL& request_url,
bool download_to_file) {
// Compute a unique request_id for this renderer process.
int id = MakeRequestID();
pending_requests_[id] = PendingRequestInfo(callback,
resource_type,
origin_pid,
frame_origin,
request_url,
download_to_file);
return id;
}

这个函数定义在文件external/chromium_org/content/child/resource_dispatcher.cc中。

ResourceDispatcher类的成员函数AddPendingRequest首先调用成员函数MakeRequestID生成一个Request ID,接着将參数callback描写叙述的一个WebURLLoaderImpl::Context对象封装在一个PendingRequestInfo对象中,而且以上述Request ID为键值。将这个PendingRequestInfo对象保存在成员变量pending_requests_描写叙述的一个Hash Map中。

回到IPCResourceLoaderBridge类的成员函数Start中,它接下来调用成员变量dispatcher_描写叙述的ResourceDispatcher对象的成员函数message_sender获得一个IPC::Sender对象,而且通过这个IPC::Sender对象向Browser进程发送一个类型为ResourceHostMsg_RequestResource的IPC消息。用来请求Browser进程下载成员变量request_描写叙述的URL相应的网页的内容。

在Browser进程中。类型为ResourceHostMsg_RequestResource的IPC消息是由ResourceDispatcherHostImpl类的成员函数OnMessageReceived进行接收的,例如以下所看到的:

bool ResourceDispatcherHostImpl::OnMessageReceived(
const IPC::Message& message,
ResourceMessageFilter* filter) {
......
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(ResourceDispatcherHostImpl, message)
IPC_MESSAGE_HANDLER(ResourceHostMsg_RequestResource, OnRequestResource)
......
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP() ......
}

这个函数定义在文件external/chromium_org/content/browser/loader/resource_dispatcher_host_impl.cc中。

ResourceDispatcherHostImpl类的成员函数OnMessageReceived将类型为ResourceHostMsg_RequestResource的IPC消息分发给另外一个成员函数OnRequestResource处理,后者的实现例如以下所看到的:

void ResourceDispatcherHostImpl::OnRequestResource(
int routing_id,
int request_id,
const ResourceHostMsg_Request& request_data) {
BeginRequest(request_id, request_data, NULL, routing_id);
}

这个函数定义在文件external/chromium_org/content/browser/loader/resource_dispatcher_host_impl.cc中。

ResourceDispatcherHostImpl类的成员函数OnRequestResource调用另外一个成员函数BeginRequest開始下载參数request_data描写叙述的URL相应的网页内容,后者的实现例如以下所看到的:

void ResourceDispatcherHostImpl::BeginRequest(
int request_id,
const ResourceHostMsg_Request& request_data,
IPC::Message* sync_result, // only valid for sync
int route_id) {
...... // Construct the request.
net::CookieStore* cookie_store =
GetContentClient()->browser()->OverrideCookieStoreForRenderProcess(
child_id);
scoped_ptr<net::URLRequest> new_request;
new_request = request_context->CreateRequest(
request_data.url, request_data.priority, NULL, cookie_store);
...... scoped_ptr<ResourceHandler> handler(
CreateResourceHandler(
new_request.get(),
request_data, sync_result, route_id, process_type, child_id,
resource_context)); if (handler)
BeginRequestInternal(new_request.Pass(), handler.Pass());
}

这个函数定义在文件external/chromium_org/content/browser/loader/resource_dispatcher_host_impl.cc中。

ResourceDispatcherHostImpl类的成员函数BeginRequest首先从參数request_data取出要下载网页内容的URL。接着又将该URL封装在一个URLRequest对象中。

ResourceDispatcherHostImpl类的成员函数BeginRequest接下来又调用另外一个成员函数CreateResourceHandler创建了一个AsyncResourceHandler对象。这个AsyncResourceHandler对象用来异步接收和处理从Webserver下载回来的网页内容。

ResourceDispatcherHostImpl类的成员函数CreateResourceHandler的实现例如以下所看到的:

scoped_ptr<ResourceHandler> ResourceDispatcherHostImpl::CreateResourceHandler(
net::URLRequest* request,
const ResourceHostMsg_Request& request_data,
IPC::Message* sync_result,
int route_id,
int process_type,
int child_id,
ResourceContext* resource_context) {
// Construct the IPC resource handler.
scoped_ptr<ResourceHandler> handler;
if (sync_result) {
...... handler.reset(new SyncResourceHandler(request, sync_result, this));
} else {
handler.reset(new AsyncResourceHandler(request, this)); // The RedirectToFileResourceHandler depends on being next in the chain.
if (request_data.download_to_file) {
handler.reset(
new RedirectToFileResourceHandler(handler.Pass(), request));
}
} ...... // Install a CrossSiteResourceHandler for all main frame requests. This will
// let us check whether a transfer is required and pause for the unload
// handler either if so or if a cross-process navigation is already under way.
bool is_swappable_navigation =
request_data.resource_type == ResourceType::MAIN_FRAME;
// If we are using --site-per-process, install it for subframes as well.
if (!is_swappable_navigation &&
CommandLine::ForCurrentProcess()->HasSwitch(switches::kSitePerProcess)) {
is_swappable_navigation =
request_data.resource_type == ResourceType::SUB_FRAME;
}
if (is_swappable_navigation && process_type == PROCESS_TYPE_RENDERER)
handler.reset(new CrossSiteResourceHandler(handler.Pass(), request)); // Insert a buffered event handler before the actual one.
handler.reset(
new BufferedResourceHandler(handler.Pass(), this, request)); ...... handler.reset(
new ThrottlingResourceHandler(handler.Pass(), request, throttles.Pass())); return handler.Pass();
}

这个函数定义在文件external/chromium_org/content/browser/loader/resource_dispatcher_host_impl.cc中。

从前面的调用过程能够知道。參数sync_result的值等于NULL。因此ResourceDispatcherHostImpl类的成员函数CreateResourceHandler首先创建了一个AsyncResourceHandler对象,保存在本地变量handler中,表示要通过异步方式下载參数request描写叙述的URL。

接下来ResourceDispatcherHostImpl类的成员函数CreateResourceHandler又会依据情况创建其他的Handler对象。这些Handler对象会依次连接在一起。当中,后面创建的Handler对象位于前面创建的Handler对象的前面。

下载回来的网页内容将依次被这些Handler对象处理。

这意味着下载回来的网页内容最后会被最先创建的AsyncResourceHandler对象进行处理。

为了简单起见,后面我们仅仅分析这个AsyncResourceHandler对象处理下载回来的网页内容的过程,也就是假设ResourceDispatcherHostImpl类的成员函数CreateResourceHandler返回给调用者的是一个AsyncResourceHandler对象。

回到ResourceDispatcherHostImpl类的成员函数BeginRequest中,它最后调用另外一个成员函数BeginRequestInternal下载本地变量new_request描写叙述的URL相应的网页内容,例如以下所看到的:

void ResourceDispatcherHostImpl::BeginRequestInternal(
scoped_ptr<net::URLRequest> request,
scoped_ptr<ResourceHandler> handler) {
...... ResourceRequestInfoImpl* info =
ResourceRequestInfoImpl::ForRequest(request.get());
...... linked_ptr<ResourceLoader> loader(
new ResourceLoader(request.Pass(), handler.Pass(), this)); ..... StartLoading(info, loader);
}

这个函数定义在文件external/chromium_org/content/browser/loader/resource_dispatcher_host_impl.cc中。

ResourceDispatcherHostImpl类的成员函数BeginRequestInternal将參数request描写叙述的URL和參数handler描写叙述的AsyncResourceHandler对象封装在一个ResourceLoader对象后。调用另外一个成员函数StartLoading開始载入參数request描写叙述的URL。

ResourceDispatcherHostImpl类的成员函数StartLoading的实现例如以下所看到的:

void ResourceDispatcherHostImpl::StartLoading(
ResourceRequestInfoImpl* info,
const linked_ptr<ResourceLoader>& loader) {
...... loader->StartRequest();
}

这个函数定义在文件external/chromium_org/content/browser/loader/resource_dispatcher_host_impl.cc中。

ResourceDispatcherHostImpl类的成员函数StartLoading主要是调用參数loader描写叙述的ResourceLoader对象的成员函数StartRequest開始载入其内部封装的URL。

ResourceLoader类的成员函数StartRequest的实现例如以下所看到的:

void ResourceLoader::StartRequest() {
...... // Give the handler a chance to delay the URLRequest from being started.
bool defer_start = false;
if (!handler_->OnWillStart(request_->url(), &defer_start)) {
Cancel();
return;
} if (defer_start) {
deferred_stage_ = DEFERRED_START;
} else {
StartRequestInternal();
}
}

这个函数定义在external/chromium_org/content/browser/loader/resource_loader.cc中。

ResourceLoader类的成员变量handler_描写叙述的便是前面我们假设ResourceDispatcherHostImpl类的成员函数CreateResourceHandler返回的AsyncResourceHandler对象。ResourceLoader类的成员函数StartRequest调用这个AsyncResourceHandler对象的成员函数OnWillStart询问是要取消、延迟、还是立即下载当前正在处理的ResourceLoader对象封装的URL相应的网页内容。

我们假设是第三种情况,这时候ResourceLoader类的成员函数StartRequest就会立即调用另外一个成员函数StartRequestInternal下载当前正在处理的ResourceLoader对象封装的URL相应的网页内容。

ResourceLoader类的成员函数StartRequestInternal的实现例如以下所看到的:

void ResourceLoader::StartRequestInternal() {
...... request_->Start(); ......
}

这个函数定义在external/chromium_org/content/browser/loader/resource_loader.cc中。

ResourceLoader类的成员变量request_描写叙述的是前面在ResourceDispatcherHostImpl类的成员函数BeginRequest中创建的一个URLRequest对象。这个URLRequest对象封装了要下载的URL。ResourceLoader类的成员函数StartRequestInternal通过调用这个URLRequest对象的成员函数Start就能够启动下载网页的过程了。

URLRequest类是Chromium在Net模块中提供的一个类,用来运行具体的网络操作,也就是依据约定的协议请求Webserver返回指定URL相应的网页的内容。这个过程我们留给读者自行分析。

Webserver响应了请求之后。Chromium的Net模块会调用ResourceLoader类的成员函数OnResponseStarted,它的实现例如以下所看到的:

void ResourceLoader::OnResponseStarted(net::URLRequest* unused) {
...... if (request_->status().is_success()) {
StartReading(false); // Read the first chunk.
} ......
}

这个函数定义在external/chromium_org/content/browser/loader/resource_loader.cc中。

ResourceLoader类的成员函数OnResponseStarted检查Webserver的响应是否成功。比如Webserver是否依据HTTP协议返回了200响应。假设成功的话。那么接下来就会调用另外一个成员函数StartReading读出第一块数据。

ResourceLoader类的成员函数StartReading的实现例如以下所看到的:

void ResourceLoader::StartReading(bool is_continuation) {
int bytes_read = 0;
ReadMore(&bytes_read); ...... if (!is_continuation || bytes_read <= 0) {
OnReadCompleted(request_.get(), bytes_read);
} else {
// Else, trigger OnReadCompleted asynchronously to avoid starving the IO
// thread in case the URLRequest can provide data synchronously.
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&ResourceLoader::OnReadCompleted,
weak_ptr_factory_.GetWeakPtr(),
request_.get(),
bytes_read));
}
}

这个函数定义在external/chromium_org/content/browser/loader/resource_loader.cc中。

ResourceLoader类的成员函数StartReading调用成员函数ReadMore读取Webserver返回来的数据。读出来的数据大小保存在本地变量bytes_read中。

ResourceLoader类的成员函数ReadMore的实现例如以下所看到的:

void ResourceLoader::ReadMore(int* bytes_read) {
...... scoped_refptr<net::IOBuffer> buf;
int buf_size;
if (!handler_->OnWillRead(&buf, &buf_size, -1)) {
Cancel();
return;
} ...... request_->Read(buf.get(), buf_size, bytes_read); ......
}

这个函数定义在external/chromium_org/content/browser/loader/resource_loader.cc中。

ResourceLoader类的成员函数ReadMore首先调用成员变量handler_描写叙述的一个AsyncResourceHandler对象的成员函数OnWillRead获取一个Buffer。这个Buffer用来保存从Webserver返回来的数据。这些数据能够通过调用ResourceLoader类的成员变量reqeust_描写叙述的一个URLRequest对象的成员函数Read获得。

AsyncResourceHandler对象的成员函数OnWillRead的实现例如以下所看到的:

bool AsyncResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf,
int* buf_size,
int min_size) {
...... if (!EnsureResourceBufferIsInitialized())
return false; ......
char* memory = buffer_->Allocate(&allocation_size_);
..... *buf = new DependentIOBuffer(buffer_.get(), memory);
*buf_size = allocation_size_; ...... return true;
}

这个函数定义在文件external/chromium_org/content/browser/loader/async_resource_handler.cc中。

AsyncResourceHandler对象的成员函数OnWillRead首先调用成员函数EnsureResourceBufferIsInitialized确保成员变量buffer_指向了一块共享内存,然后再从这块共享内存中分配一块大小等于成员变量allocation_size_的值的缓冲区,用来返回给调用者保存从Webserver返回来的数据。

AsyncResourceHandler类的成员函数EnsureResourceBufferIsInitialized的实现例如以下所看到的:

bool AsyncResourceHandler::EnsureResourceBufferIsInitialized() {
if (buffer_.get() && buffer_->IsInitialized())
return true; ...... buffer_ = new ResourceBuffer();
return buffer_->Initialize(kBufferSize,
kMinAllocationSize,
kMaxAllocationSize);
}

这个函数定义在文件external/chromium_org/content/browser/loader/async_resource_handler.cc中。

AsyncResourceHandler类的成员函数EnsureResourceBufferIsInitialized首先检查成员变量buffer_是否指向了一个ResourceBuffer对象,而且这个ResourceBuffer对象描写叙述的共享内存是否已经创建。

假设AsyncResourceHandler类的成员变量buffer_还没有指向一个ResourceBuffer对象,或者指向了一个ResourceBuffer对象,可是这个ResourceBuffer对象描写叙述的共享内存还没有创建。那么AsyncResourceHandler类的成员函数EnsureResourceBufferIsInitialized就会创建一个ResourceBuffer对象保存在成员变量buffer_中。而且调用这个ResourceBuffer对象的成员函数Initialize创建一块大小为kBufferSize的共享内存。

这块共享内存每次能够分配出来的缓冲区最小值为kMinAllocationSize,最大值为kMaxAllocationSize。

在Android平台上,调用ResourceBuffer类的成员函数Initialize创建的共享内存实际上是匿名共享内存。匿名共享内存能够通过Binder机制在两个进程之间进行共享。

这一点能够參考前面Android系统匿名共享内存Ashmem(Anonymous Shared Memory)在进程间共享的原理分析一文。这样Browser进程就能够通过这块匿名共享内存将下载回来的网页内容传递给Render进程处理。

这一步运行完毕后,回到ResourceLoader类的成员函数StartReading中,假设没有读出数据(表明数据已经下载完毕),或者參数is_continuation的值等于false(表示读出来的是第一个数据块),那么ResourceLoader类的成员函数StartReading就会调用成员函数OnReadCompleted立即进行下一步处理。其余情况下。为了避免当前(网络)线程被堵塞,ResourceLoader类的成员函数StartReading并不会立即调用成员函数OnReadCompleted处理读出来的数据,而是延后一个消息处理。也就是等ResourceLoader类的成员函数StartReading返回到Chromium的Net模块之后再作处理。

接下来我们继续分析ResourceLoader类的成员函数OnReadCompleted的实现。例如以下所看到的:

void ResourceLoader::OnReadCompleted(net::URLRequest* unused, int bytes_read) {
...... CompleteRead(bytes_read); ...... if (bytes_read > 0) {
StartReading(true); // Read the next chunk.
} else {
// URLRequest reported an EOF. Call ResponseCompleted.
DCHECK_EQ(0, bytes_read);
ResponseCompleted();
}
}

这个函数定义在external/chromium_org/content/browser/loader/resource_loader.cc中。

ResourceLoader类的成员函数OnReadCompleted首先调用成员函数CompleteRead处理当前读出来的数据,数据的大小由參数bytes_read描写叙述。假设当前读出来的数据的大小大于0。那么就表示数据还没读完,这时候就须要调用前面分析的成员函数StartReading继续进行读取。

注意,这时候传递成员函数StartReading的參数为true,表示不是第一次读取Webserver返回来的数据。

还有一方面,假设当前读出来的数据的大小小于等于0,那么就说明Webserver已经把全部的数据都返回来了,这时候ResourceLoader类的成员函数OnReadCompleted就调用另外一个成员函数ResponseCompleted结束读取数据。

接下来我们继续分析ResourceLoader类的成员函数CompleteRead的实现,以便了解Browser进程将下载回来的网页内容返回给Render进程处理的过程。例如以下所看到的:

void ResourceLoader::CompleteRead(int bytes_read) {
...... bool defer = false;
if (!handler_->OnReadCompleted(bytes_read, &defer)) {
Cancel();
} ......
}

这个函数定义在external/chromium_org/content/browser/loader/resource_loader.cc中。

ResourceLoader类的成员函数CompleteRead将读取出来的数据交给成员变量handler_描写叙述的一个AsyncResourceHandler对象处理。这是通过调用它的成员函数OnReadCompleted实现的。

AsyncResourceHandler类的成员函数OnReadCompleted的实现例如以下所看到的:

bool AsyncResourceHandler::OnReadCompleted(int bytes_read, bool* defer) {
...... if (!sent_first_data_msg_) {
base::SharedMemoryHandle handle;
int size;
if (!buffer_->ShareToProcess(filter->PeerHandle(), &handle, &size))
return false;
filter->Send(new ResourceMsg_SetDataBuffer(
GetRequestID(), handle, size, filter->peer_pid()));
sent_first_data_msg_ = true;
} int data_offset = buffer_->GetLastAllocationOffset(); int64_t current_transfer_size = request()->GetTotalReceivedBytes();
int encoded_data_length = current_transfer_size - reported_transfer_size_;
reported_transfer_size_ = current_transfer_size; filter->Send(new ResourceMsg_DataReceived(
GetRequestID(), data_offset, bytes_read, encoded_data_length)); ......
}

这个函数定义在文件external/chromium_org/content/browser/loader/async_resource_handler.cc。

当AsyncResourceHandler类的成员变量sent_first_data_msg_的值等于false的时候。表示当前正在处理的AsyncResourceHandler对象还没有向Render进程返回过从Webserver下载回来的网页内容。

这时候AsyncResourceHandler类的成员函数OnReadCompleted首先要向Render进程发送一个类型为ResourceMsg_SetDataBuffer的IPC消息。

这个IPC消息会将AsyncResourceHandler类的成员变量buffer_描写叙述的共享内存传递给Render进程,以便Render进程接下来能够通过这块共享内存读取从Webserver下载回来的网页内容。

最后,AsyncResourceHandler类的成员函数OnReadCompleted再向Render进程发送一个类型为ResourceMsg_DataReceived的IPC消息。

这个IPC消息告诉Render进程从前面所描写叙述的共享内存的什么位置開始读取多少数据。有了这些数据之后,Render进程就能够构建网页的DOM Tree了。

接下来我们就继续分析Render进程接收和处理类型为ResourceMsg_SetDataBuffer和ResourceMsg_DataReceived的IPC消息的过程。

Render进程是通过ResourceDispatcher类的成员函数DispatchMessage接收类型为ResourceMsg_SetDataBuffer和ResourceMsg_DataReceived的IPC消息的,例如以下所看到的:

void ResourceDispatcher::DispatchMessage(const IPC::Message& message) {
IPC_BEGIN_MESSAGE_MAP(ResourceDispatcher, message)
......
IPC_MESSAGE_HANDLER(ResourceMsg_SetDataBuffer, OnSetDataBuffer)
IPC_MESSAGE_HANDLER(ResourceMsg_DataReceived, OnReceivedData)
......
IPC_END_MESSAGE_MAP()
}

这个函数定义在文件external/chromium_org/content/child/resource_dispatcher.cc中。

从这里能够看到,ResourceDispatcher类的成员函数DispatchMessage把类型为ResourceMsg_SetDataBuffer的IPC消息分发给成员函数OnSetDataBuffer处理。把类型为ResourceMsg_DataReceived的IPC消息分发给成员函数OnReceivedData处理。

ResourceDispatcher类的成员函数OnSetDataBuffer的实现例如以下所看到的:

void ResourceDispatcher::OnSetDataBuffer(int request_id,
base::SharedMemoryHandle shm_handle,
int shm_size,
base::ProcessId renderer_pid) {
......
PendingRequestInfo* request_info = GetPendingRequestInfo(request_id);
...... request_info->buffer.reset(
new base::SharedMemory(shm_handle, true)); // read only bool ok = request_info->buffer->Map(shm_size);
...... request_info->buffer_size = shm_size;
}

这个函数定义在文件external/chromium_org/content/child/resource_dispatcher.cc中。

从前面的分析能够知道,Render进程在请求Browser进程下载指定URL相应的网页内容之前,会创建一个PendingRequestInfo对象。这个PendingRequestInfo对象以一个Request ID为键值保存在ResourceDispatcher类的内部。这个Request ID即为參数request_id描写叙述的Request ID。

因此。ResourceDispatcher类的成员函数OnSetDataBuffer能够通过參数request_id获得一个PendingRequestInfo对象。有了这个PendingRequestInfo对象之后。ResourceDispatcher类的成员函数OnSetDataBuffer就依据參数shm_handle描写叙述的句柄创建一个ShareMemory对象,保存在它的成员变量buffer中。

ResourceDispatcher类的成员函数OnSetDataBuffer最后调用上述ShareMemory对象的成员函数Map就可以将Browser进程传递过来的共享内存映射到当前进程的地址空间来,这样以后就能够直接从这块共享内存读出Browser进程下载回来的网页内容。

ResourceDispatcher类的成员函数OnReceivedData的实现例如以下所看到的:

void ResourceDispatcher::OnReceivedData(int request_id,
int data_offset,
int data_length,
int encoded_data_length) {
......
PendingRequestInfo* request_info = GetPendingRequestInfo(request_id);
......
if (request_info && data_length > 0) {
......
linked_ptr<base::SharedMemory> retain_buffer(request_info->buffer);
...... const char* data_start = static_cast<char*>(request_info->buffer->memory());
......
const char* data_ptr = data_start + data_offset;
...... // Check whether this response data is compliant with our cross-site
// document blocking policy. We only do this for the first packet.
std::string alternative_data;
if (request_info->site_isolation_metadata.get()) {
request_info->blocked_response =
SiteIsolationPolicy::ShouldBlockResponse(
request_info->site_isolation_metadata, data_ptr, data_length,
&alternative_data);
request_info->site_isolation_metadata.reset(); // When the response is blocked we may have any alternative data to
// send to the renderer. When |alternative_data| is zero-sized, we do not
// call peer's callback.
if (request_info->blocked_response && !alternative_data.empty()) {
data_ptr = alternative_data.data();
data_length = alternative_data.size();
encoded_data_length = alternative_data.size();
}
} if (!request_info->blocked_response || !alternative_data.empty()) {
if (request_info->threaded_data_provider) {
request_info->threaded_data_provider->OnReceivedDataOnForegroundThread(
data_ptr, data_length, encoded_data_length);
// A threaded data provider will take care of its own ACKing, as the
// data may be processed later on another thread.
send_ack = false;
} else {
request_info->peer->OnReceivedData(
data_ptr, data_length, encoded_data_length);
}
} ......
} ......
}

这个函数定义在文件external/chromium_org/content/child/resource_dispatcher.cc中。

ResourceDispatcher类的成员函数OnReceivedData首先获得參数request_id相应的一个PendingRequestInfo对象。保存在本地变量request_info中。有了这个PendingRequestInfo对象之后,就能够依据參数data_offset和data_length从它的成员变量buffer描写叙述的共享内存中获得Browser进程下载回来的网页内容。

假设这是一个跨站(cross-site)请求下载回来的内容。ResourceDispatcher类的成员函数OnReceivedData会调用SiteIsolationPolicy类的静态成员函数ShouldBlockResponse依据Cross-Site Document Blocking Policy决定是否须要阻止下载回来的内容在当前Render进程中载入。关于Chromium的Cross-Site Document Blocking Policy,能够參考Site IsolationBlocking Cross-Site Documents for Site Isolation这两篇文章。

假设SiteIsolationPolicy类的静态成员函数ShouldBlockResponse表明要阻止下载回来的内容在当前Render进程中载入。那么本地变量request_info指向的PendingRequestInfo对象的成员变量blocked_response的值就会等于true。这时候假设SiteIsolationPolicy类的静态成员函数ShouldBlockResponse还返回了Alternative Data,那么这个Alternative Data就会替换下载回来的网页内容交给WebKit处理。

假设SiteIsolationPolicy类的静态成员函数ShouldBlockResponse没有阻止下载回来的内容在当前Render进程中载入,或者阻止的同一时候也提供了Alternative Data,那么ResourceDispatcher类的成员函数OnReceivedData接下来继续推断本地变量request_info指向的PendingRequestInfo对象的成员变量threaded_data_provider是否指向了一个ThreadedDataProvider对象。假设指向了一个ThreadedDataProvider对象,那么ResourceDispatcher类的成员函数OnReceivedData会将下载回来的网页内容交给这个ThreadedDataProvider对象的成员函数OnReceivedDataOnForegroundThread处理。否则的话,下载回来的网页内容将会交给本地变量request_info指向的PendingRequestInfo对象的成员变量peer描写叙述的一个WebURLLoaderImpl::Context对象的成员函数OnReceivedData处理。

WebKit在请求Chromium的Content模块下载指定URL相应的网页内容时,能够指定将下载回来的网页内容交给一个后台线程进行接收和解析,这时候本地变量request_info指向的PendingRequestInfo对象的成员变量threaded_data_provider就会指向一个ThreadedDataProvider对象。这个ThreadedDataProvider对象就会将下载回来的网页内容交给一个后台线程接收和解析。我们不考虑这样的情况。因此接下来我们继续分析WebURLLoaderImpl::Context类的成员函数OnReceivedData的实现,例如以下所看到的:

void WebURLLoaderImpl::Context::OnReceivedData(const char* data,
int data_length,
int encoded_data_length) {
...... if (ftp_listing_delegate_) {
// The FTP listing delegate will make the appropriate calls to
// client_->didReceiveData and client_->didReceiveResponse.
ftp_listing_delegate_->OnReceivedData(data, data_length);
} else if (multipart_delegate_) {
// The multipart delegate will make the appropriate calls to
// client_->didReceiveData and client_->didReceiveResponse.
multipart_delegate_->OnReceivedData(data, data_length, encoded_data_length);
} else {
client_->didReceiveData(loader_, data, data_length, encoded_data_length);
}
}

这个函数定义在文件external/chromium_org/content/child/web_url_loader_impl.cc中。

当从Webserver返回来的网页内容的MIME类型为“text/vnd.chromium.ftp-dir”时,WebURLLoaderImpl::Context类的成员变量ftp_listing_delegate_指向一个FtpDirectoryListingResponseDelegate对象。

这时候从Webserver返回来的网页内容是一些FTP文件夹,上述FtpDirectoryListingResponseDelegate对象对这些网页内容进行一些排版处理后,再交给WebKit处理,也就是ResourceLoader类的成员变量client_描写叙述的一个ResourceLoader对象处理。

当从Webserver返回来的网页内容的MIME类型为“multipart/x-mixed-replace”时,WebURLLoaderImpl::Context类的成员变量multipart_delegate_指向一个MultipartResponseDelegate对象。

这时候从Webserver返回来的网页内容包括若干个数据块,每个数据块都有单独的MIME类型。而且它们之间通过一个Boundary String。上述MultipartResponseDelegate对象依据Boundary String解析出每一数据块之后,再交给WebKit处理,也就是ResourceLoader类的成员变量client_描写叙述的一个ResourceLoader对象处理。

在其余情况下,WebURLLoaderImpl::Context类的成员函数OnReceivedData直接把Webserver返回来的网页内容交给WebKit处理,也就是调用ResourceLoader类的成员变量client_描写叙述的一个ResourceLoader对象的成员函数didReceiveData进行处理。

至此,我们就分析完毕Chromium下载指定URL相应的网页内容的过程了。

下载回来的网页内容将由WebKit进行处理,也就是由ResourceLoader类的成员函数didReceiveData进行处理。

这个处理过程即为网页内容的解析过程,解析后就会得到一棵DOM Tree。有了DOM Tree之后,接下来就能够对下载回来的网页内容进行渲染了。在接下来的一篇文章中,我们再具体分析WebKit依据网页内容生成DOM Tree的过程,敬请关注!

很多其他的信息也能够关注老罗的新浪微博:http://weibo.com/shengyangluo

Chromium网页URL载入过程分析的更多相关文章

  1. Chromium网页Frame Tree创建过程分析

         Chromium在加载一个网页之前,需要在Browser进程创建一个Frame Tree.Browser进程为网页创建了Frame Tree之后,再请求Render进程加载其内容.Frame ...

  2. Chromium网页Layer Tree创建过程分析

    在Chromium中.WebKit会创建一个Graphics Layer Tree描写叙述网页.Graphics Layer Tree是和网页渲染相关的一个Tree. 网页渲染终于由Chromium的 ...

  3. Chromium网页Graphics Layer Tree创建过程分析

    在前面一文中.我们分析了网页Render Layer Tree的创建过程.在创建Render Layer的同一时候,WebKit还会为其创建Graphics Layer.这些Graphics Laye ...

  4. Chromium网页输入事件捕捉和手势检測过程分析

    连续的输入事件可能会产生一定的手势操作.比如滑动手势和捏合手势. 在Chromium中,网页的输入事件是在Browser进程中捕捉的.Browser进程捕获输入事件之后,会进行手势操作检測.检測出来的 ...

  5. JavaScript如何获取网页url中的参数

    我们可以自定义一个公共函数来实现网页url中的参数获取,返回的是一个数组 GetUrlRequest: function () { var url = decodeURI(location.searc ...

  6. 获取网页URL地址及参数等的两种方法(js和C#)

    转:获取网页URL地址及参数等的两种方法(js和C#) 一 js 先看一个示例 用javascript获取url网址信息 <script type="text/javascript&q ...

  7. 爬虫_网页url设计

    为什么需要网页URL设计? 每个url不同的结构代表着不同的网页模块和信息的展现形式,为了方便维护与管理 网页url怎么设计? 分层: 主域名,子域名 一般形式为: 主域名:  www.job.com ...

  8. 可操纵网页URL地址的js插件-url.js

    url.js是一款能够很有用方便的操纵网页URL地址的js插件.通过url.js你能够设置和获取当前URL的參数,也能够对当前URL的參数进行更新.删除操作.还能够将当前URL的參数显示为json字符 ...

  9. LAMP环境下,通过网页url获取gb2312编码中文命名的下载资源方法

    最近有个功能, 要求获取中文命名的.zip压缩文件,我准备直接采用网页url填写压缩文件地址的方式获取下载资源, 但问题是 我们的linux系统和php编程环境都是采用的zh_GB2312编码, 而浏 ...

随机推荐

  1. python 内存中写入文件(read读取不到文件解决)

    from io import StringIO a = StringIO.StringIO('title') a.write('content1\n') a.write('content2') a.s ...

  2. 交换工资 SQL

  3. 10款jQuery/CSS3动画应用 超有用

    http://www.html5tricks.com/10-jquery-css3-animation.html

  4. android抓取各种log的方法

    1.logcat (四类log buffer是main,radio.system.events) adb wait-for-device logcat adb logcat -v time > ...

  5. golang sync.Mutex

    //go func 和主线程之间的关系是并行和竞争关系 package main import ( "fmt" "sync" "time" ...

  6. powerdesigner 连接mysql提示“connection test failed”

    powerdesigner  连接mysql提示“connection test failed”,该如何解决: 1.把64位的jdk换成32位的jdk(VM只支持32的jre) 2.系统变量:  CL ...

  7. C语言速度优化之指针赋值与if推断

    近期在写的一个项目须要优化处理速度,我写了一下程序来測试指针赋值与指针推断的速度比較.结果让我大吃一惊. #include <stdio.h> #include <stdlib.h& ...

  8. c3p0的经常使用配置方式

    1:第一种方式很easy c3p0.driverClass=com.mysql.jdbc.Driver c3p0.jdbcUrl=jdbc:mysql://localhost:3308/databas ...

  9. Bitmap-把方形图片处理为圆形

    这个是直接在网上转载的,自己验证可靠 转载自http://my.oschina.net/zhouz/blog/213164 直接贴上代码 import android.graphics.Bitmap; ...

  10. 动态规划例子:Maximal Square

    Given a 2D binary matrix filled with 0's and 1's, find the largest square containing all 1's and ret ...