配色: 字号:
Chromium网页URL加载过程分析
2016-10-25 | 阅:  转:  |  分享 
  
Chromium网页URL加载过程分析

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



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



这是因为Render进程是通过WebKit加载网页URL的,WebKit不关心自己所在的进程是否有网络访问权限,它通过特定的接口访问网络。这个特定接口由WebKit的使用者,也就是Render进程中的Content模块实现。Content模块在实现这个接口的时候,会通过IPC请求Browser进程下载网络的内容。这种设计方式使得WebKit可以灵活地使用:既可以在有网络访问权限的进程中使用,也可以在没有网络访问权限的进程中使用,并且使用方式是统一的。



从前面一文可以知道,Browser进程中为要加载的网页创建了一个FrameTree之后,会向Render进程发送一个类型为FrameMsg_Navigate的IPC消息。Render进程接收到这个IPC消息之后,处理流程如图1所示:



Render进程执行了一些初始化工作之后,就向Browser进程发送一个类型为ResourceHostMsg_RequestResource的IPC消息。Browser进程收到这个IPC消息之后,就会通过HTTP协议请求Web服务器将网页的内容返回来。请求得到响应后,Browser进程就会创建一块共享内存,并且通过一个类型为ResourceMsg_SetDataBuffer的IPC消息将这块共享内存传递给Render进程的。



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



接下来,我们就从Render进程接收类型为FrameMsg_Navigate的IPC消息开始分析网页URL的加载过程。Render进程是通过RenderFrameImpl类的成员函数OnMessageReceived接收类型为FrameMsg_Navigate的IPC消息的,如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

boolRenderFrameImpl::OnMessageReceived(constIPC::Message&msg){

......



boolhandled=true;

IPC_BEGIN_MESSAGE_MAP(RenderFrameImpl,msg)

IPC_MESSAGE_HANDLER(FrameMsg_Navigate,OnNavigate)

......

IPC_END_MESSAGE_MAP()



returnhandled;

}

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

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



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

voidRenderFrameImpl::OnNavigate(constFrameMsg_Navigate_Params¶ms){

......



boolis_reload=RenderViewImpl::IsReload(params);

......



WebFrameframe=frame_;

......



if(is_reload){

......

}elseif(params.page_state.IsValid()){

......

}elseif(!params.base_url_for_data_url.is_empty()){

......

}else{

//NavigatetothegivenURL.

WebURLRequestrequest(params.url);

......



frame->loadRequest(request);



......

}



......

}

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

从前面一文可以知道,RenderFrameImpl类的成员变量frame_指向的是一个WebLocalFrameImpl对象。如果当前正在处理的RenderFrameImpl对象还没有加载过URL,并且当前要加载的URL不为空,RenderFrameImpl类的成员函数OnNavigate会调用成员变量frame_指向的WebLocalFrameImpl对象的成员函数loadRequest加载指定的URL。



WebLocalFrameImpl类的成员函数loadRequest的实现如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

voidWebLocalFrameImpl::loadRequest(constWebURLRequest&request)

{

......

constResourceRequest&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对象的创建过程,可以参考前面一文。接下来我们继续分析FrameLoader类的成员函数load的实现,如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

voidFrameLoader::load(constFrameLoadRequest&passedRequest)

{

......



FrameLoadRequestrequest(passedRequest);

......



FrameLoadTypenewLoadType=determineFrameLoadType(request);

NavigationActionaction(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的实现如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

voidFrameLoader::loadWithNavigationAction(constNavigationAction&action,FrameLoadTypetype,PassRefPtrWillBeRawPtrformState,constSubstituteData&substituteData,ClientRedirectPolicyclientRedirect,constAtomicString&overrideEncoding)

{

......



constResourceRequest&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的实现,可以参考前面一文。



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



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



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

voidDocumentLoader::startLoadingMainResource()

{

......



FetchRequestcachedResourceRequest(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中。

从前面一文可以知道,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继承下来的,它的实现如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

voidResource::addClient(ResourceClientclient)

{

if(addClientToSet(client))

didAddClient(client);

}

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

Resource类的成员函数addClient调用另外一个成员函数addClientToSet将参数client描述的一个DocumentLoader对象保存在内部,如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

boolResource::addClientToSet(ResourceClientclient)

{

......



m_clients.add(client);

returntrue;

}

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

Resource类的成员函数addClientToSet将参数client描述的一个DocumentLoader保存在成员变量m_clients描述的一个HashSet中,以便当前正在处理的Resource对象描述的网页内容从Web服务器下载回来的时候,可以交给它处理。



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



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

ResourcePtrResourceFetcher::fetchMainResource(FetchRequest&request,constSubstituteData&substituteData)

{

......

returntoRawResource(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的实现如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

ResourcePtrResourceFetcher::requestResource(Resource::Typetype,FetchRequest&request)

{

......



KURLurl=request.resourceRequest().url();

......



constRevalidationPolicypolicy=determineRevalidationPolicy(type,request.mutableResourceRequest(),request.forPreload(),resource.get(),request.defer(),request.options());

switch(policy){

......

caseLoad:

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());



......

}



......



returnresource;

}

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

ResourceFetcher类的成员函数requestResource首先调用成员函数createResourceForLoading为参数request描述的URL创建一个RawResource对象,如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

ResourcePtrResourceFetcher::createResourceForLoading(Resource::Typetype,FetchRequest&request,constString&charset)

{

......



ResourcePtrresource=createResource(type,request.resourceRequest(),charset);



......

returnresource;

}

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

ResourceFetcher类的成员函数createResourceForLoading调用函数createResource根据参数type和request创建一个RawResource对象,如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

staticResourcecreateResource(Resource::Typetype,constResourceRequest&request,constString&charset)

{

switch(type){

......

caseResource::MainResource:

caseResource::Raw:

caseResource::TextTrack:

caseResource::Media:

returnnewRawResource(request,type);

......

}



......

return0;

}

这个函数定义在文件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从Web服务器下载对应的网页内容。



我们假设request描述的URL需要进行加载,并且不是一个存档文件,因此接下来我们继续分析RawResource类的成员函数load的实现。RawResource类的成员函数load是从父类Resource继承下来的,它的实现如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

voidResource::load(ResourceFetcherfetcher,constResourceLoaderOptions&options)

{

......



ResourceRequestrequest(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对象,如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

PassRefPtrResourceLoader::create(ResourceLoaderHosthost,Resourceresource,constResourceRequest&request,constResourceLoaderOptions&options)

{

RefPtrloader(adoptRef(newResourceLoader(host,resource,options)));

loader->init(request);

returnloader.release();

}

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

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



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



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

voidResourceLoader::start()

{

......



m_loader=adoptPtr(blink::Platform::current()->createURLLoader());

......

blink::WrappedResourceRequestwrappedRequest(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的实现如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

WebURLLoaderBlinkPlatformImpl::createURLLoader(){

returnnewWebURLLoaderImpl;

}

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

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



接下来我们继续分析WebURLLoaderImpl类的成员函数loadAsynchronously异步加载一个URL的过程,如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

voidWebURLLoaderImpl::loadAsynchronously(constWebURLRequest&request,

WebURLLoaderClientclient){

......



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实现的,如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

classWebURLLoaderImpl::Context:publicbase::RefCounted,

publicRequestPeer{

public:

......



voidset_client(WebURLLoaderClientclient){client_=client;}



private:

......



WebURLLoaderClientclient_;



......

};

这个函数定义在文件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,如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

voidWebURLLoaderImpl::Context::Start(constWebURLRequest&request,

SyncLoadResponsesync_load_response){

......



GURLurl=request.url();

......



RequestInforequest_info;

......

request_info.url=url;

......

bridge_.reset(ChildThread::current()->resource_dispatcher()->CreateBridge(

request_info));



......



if(bridge_->Start(this)){

AddRef();//BalancedinOnCompletedRequest

}else{

bridge_.reset();

}

}

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

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



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

classCONTENT_EXPORTChildThread

:publicIPC::Listener,

publicIPC::Sender,

publicNON_EXPORTED_BASE(mojo::ServiceProvider){

public:

......



ResourceDispatcherresource_dispatcher()const{

returnresource_dispatcher_.get();

}



......



private:

......



//Handlesresourceloadsforthisprocess.

scoped_ptrresource_dispatcher_;



......

};

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

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



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



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

ResourceLoaderBridgeResourceDispatcher::CreateBridge(

constRequestInfo&request_info){

returnnewIPCResourceLoaderBridge(this,request_info);

}

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

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



回到WebURLLoaderImpl::Context类的成员函数Start中,它获得了一个IPCResourceLoaderBridge对象之后,接着调用这个IPCResourceLoaderBridge对象的成员函数Start加载参数request描述的URL,如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

boolIPCResourceLoaderBridge::Start(RequestPeerpeer){

......



//generatetherequestID,andappendittothemessage

request_id_=dispatcher_->AddPendingRequest(peer,

request_.resource_type,

request_.origin_pid,

frame_origin_,

request_.url,

request_.download_to_file);



returndispatcher_->message_sender()->Send(

newResourceHostMsg_RequestResource(routing_id_,request_id_,request_));

}

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

IPCResourceLoaderBridge类的成员变量dispatcher_描述的是一个ResourceDispatcher对象,IPCResourceLoaderBridge类的成员函数Start首先调用这个ResourceDispatcher对象的成员函数AddPendingRequest将参数peer描述的一个WebURLLoaderImpl::Context对象保存在内部,如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

intResourceDispatcher::AddPendingRequest(RequestPeercallback,

ResourceType::Typeresource_type,

intorigin_pid,

constGURL&frame_origin,

constGURL&request_url,

booldownload_to_file){

//Computeauniquerequest_idforthisrendererprocess.

intid=MakeRequestID();

pending_requests_[id]=PendingRequestInfo(callback,

resource_type,

origin_pid,

frame_origin,

request_url,

download_to_file);

returnid;

}

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

ResourceDispatcher类的成员函数AddPendingRequest首先调用成员函数MakeRequestID生成一个RequestID,接着将参数callback描述的一个WebURLLoaderImpl::Context对象封装在一个PendingRequestInfo对象中,并且以上述RequestID为键值,将这个PendingRequestInfo对象保存在成员变量pending_requests_描述的一个HashMap中。



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



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



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

boolResourceDispatcherHostImpl::OnMessageReceived(

constIPC::Message&message,

ResourceMessageFilterfilter){

......

boolhandled=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处理,后者的实现如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

voidResourceDispatcherHostImpl::OnRequestResource(

introuting_id,

intrequest_id,

constResourceHostMsg_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对应的网页内容,后者的实现如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

voidResourceDispatcherHostImpl::BeginRequest(

intrequest_id,

constResourceHostMsg_Request&request_data,

IPC::Messagesync_result,//onlyvalidforsync

introute_id){

......



//Constructtherequest.

net::CookieStorecookie_store=

GetContentClient()->browser()->OverrideCookieStoreForRenderProcess(

child_id);

scoped_ptrnew_request;

new_request=request_context->CreateRequest(

request_data.url,request_data.priority,NULL,cookie_store);

......



scoped_ptrhandler(

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对象用来异步接收和处理从Web服务器下载回来的网页内容。



ResourceDispatcherHostImpl类的成员函数CreateResourceHandler的实现如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

scoped_ptrResourceDispatcherHostImpl::CreateResourceHandler(

net::URLRequestrequest,

constResourceHostMsg_Request&request_data,

IPC::Messagesync_result,

introute_id,

intprocess_type,

intchild_id,

ResourceContextresource_context){

//ConstructtheIPCresourcehandler.

scoped_ptrhandler;

if(sync_result){

......



handler.reset(newSyncResourceHandler(request,sync_result,this));

}else{

handler.reset(newAsyncResourceHandler(request,this));



//TheRedirectToFileResourceHandlerdependsonbeingnextinthechain.

if(request_data.download_to_file){

handler.reset(

newRedirectToFileResourceHandler(handler.Pass(),request));

}

}



......



//InstallaCrossSiteResourceHandlerforallmainframerequests.Thiswill

//letuscheckwhetheratransferisrequiredandpausefortheunload

//handlereitherifsoorifacross-processnavigationisalreadyunderway.

boolis_swappable_navigation=

request_data.resource_type==ResourceType::MAIN_FRAME;

//Ifweareusing--site-per-process,installitforsubframesaswell.

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(newCrossSiteResourceHandler(handler.Pass(),request));



//Insertabufferedeventhandlerbeforetheactualone.

handler.reset(

newBufferedResourceHandler(handler.Pass(),this,request));



......



handler.reset(

newThrottlingResourceHandler(handler.Pass(),request,throttles.Pass()));



returnhandler.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对应的网页内容,如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

voidResourceDispatcherHostImpl::BeginRequestInternal(

scoped_ptrrequest,

scoped_ptrhandler){

......



ResourceRequestInfoImplinfo=

ResourceRequestInfoImpl::ForRequest(request.get());

......



linked_ptrloader(

newResourceLoader(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的实现如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

voidResourceDispatcherHostImpl::StartLoading(

ResourceRequestInfowww.shanxiwang.netImplinfo,

constlinked_ptr&loader){

......



loader->StartRequest();

}

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

ResourceDispatcherHostImpl类的成员函数StartLoading主要是调用参数loader描述的ResourceLoader对象的成员函数StartRequest开始加载其内部封装的URL。



ResourceLoader类的成员函数StartRequest的实现如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

voidResourceLoader::StartRequest(){

......



//GivethehandlerachancetodelaytheURLRequestfrombeingstarted.

booldefer_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的实现如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

voidResourceLoader::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模块中提供的一个类,用来执行具体的网络操作,也就是根据约定的协议请求Web服务器返回指定URL对应的网页的内容。这个过程我们留给读者自行分析。



Web服务器响应了请求之后,Chromium的Net模块会调用ResourceLoader类的成员函数OnResponseStarted,它的实现如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

voidResourceLoader::OnResponseStarted(net::URLRequestunused){

......



if(request_->status().is_success()){

StartReading(false);//Readthefirstchunk.

}



......

}

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

ResourceLoader类的成员函数OnResponseStarted检查Web服务器的响应是否成功,例如Web服务器是否根据HTTP协议返回了200响应。如果成功的话,那么接下来就会调用另外一个成员函数StartReading读出第一块数据。



ResourceLoader类的成员函数StartReading的实现如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

voidResourceLoader::StartReading(boolis_continuation){

intbytes_read=0;

ReadMore(&bytes_read);



......



if(!is_continuation||bytes_read<=0){

OnReadCompleted(request_.get(),bytes_read);

}else{

//Else,triggerOnReadCompletedasynchronouslytoavoidstarvingtheIO

//threadincasetheURLRequestcanprovidedatasynchronously.

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读取Web服务器返回来的数据,读出来的数据大小保存在本地变量bytes_read中。



ResourceLoader类的成员函数ReadMore的实现如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

voidResourceLoader::ReadMore(intbytes_read){

......



scoped_refptrbuf;

intbuf_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用来保存从Web服务器返回来的数据。这些数据可以通过调用ResourceLoader类的成员变量reqeust_描述的一个URLRequest对象的成员函数Read获得。



AsyncResourceHandler对象的成员函数OnWillRead的实现如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

boolAsyncResourceHandler::OnWillRead(scoped_refptrbuf,

intbuf_size,

intmin_size){

......



if(!EnsureResourceBufferIsInitialized())

returnfalse;



......

charmemory=buffer_->Allocate(&allocation_size_);

.....



buf=newDependentIOBuffer(buffer_.get(),memory);

buf_size=allocation_size_;



......



returntrue;

}

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

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



AsyncResourceHandler类的成员函数EnsureResourceBufferIsInitialized的实现如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

boolAsyncResourceHandler::EnsureResourceBufferIsInitialized(){

if(buffer_.get()&&buffer_->IsInitialized())

returntrue;



......



buffer_=newResourceBuffer();

returnbuffer_->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机制在两个进程之间进行共享。这一点可以参考前面一文。这样Browser进程就可以通过这块匿名共享内存将下载回来的网页内容传递给Render进程处理。



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



接下来我们继续分析ResourceLoader类的成员函数OnReadCompleted的实现,如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

voidResourceLoader::OnReadCompleted(net::URLRequestunused,intbytes_read){

......



CompleteRead(bytes_read);



......



if(bytes_read>0){

StartReading(true);//Readthenextchunk.

}else{

//URLRequestreportedanEOF.CallResponseCompleted.

DCHECK_EQ(0,bytes_read);

ResponseCompleted();

}

}

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

ResourceLoader类的成员函数OnReadCompleted首先调用成员函数CompleteRead处理当前读出来的数据,数据的大小由参数bytes_read描述。如果当前读出来的数据的大小大于0,那么就表示数据还没读完,这时候就需要调用前面分析的成员函数StartReading继续进行读取。注意,这时候传递成员函数StartReading的参数为true,表示不是第一次读取Web服务器返回来的数据。



另一方面,如果当前读出来的数据的大小小于等于0,那么就说明Web服务器已经把所有的数据都返回来了,这时候ResourceLoader类的成员函数OnReadCompleted就调用另外一个成员函数ResponseCompleted结束读取数据。



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



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

voidResourceLoader::CompleteRead(intbytes_read){

......



booldefer=false;

if(!handler_->OnReadCompleted(bytes_read,&defer)){

Cancel();

}



......

}

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

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



AsyncResourceHandler类的成员函数OnReadCompleted的实现如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

boolAsyncResourceHandler::OnReadCompleted(intbytes_read,booldefer){

......



if(!sent_first_data_msg_){

base::SharedMemoryHandlehandle;

intsize;

if(!buffer_->ShareToProcess(filter->PeerHandle(),&handle,&size))

returnfalse;

filter->Send(newResourceMsg_SetDataBuffer(

GetRequestID(),handle,size,filter->peer_pid()));

sent_first_data_msg_=true;

}



intdata_offset=buffer_->GetLastAllocationOffset();



int64_tcurrent_transfer_size=request()->GetTotalReceivedBytes();

intencoded_data_length=current_transfer_size-reported_transfer_size_;

reported_transfer_size_=current_transfer_size;



filter->Send(newResourceMsg_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进程返回过从Web服务器下载回来的网页内容。这时候AsyncResourceHandler类的成员函数OnReadCompleted首先要向Render进程发送一个类型为ResourceMsg_SetDataBuffer的IPC消息。这个IPC消息会将AsyncResourceHandler类的成员变量buffer_描述的共享内存传递给Render进程,以便Render进程接下来可以通过这块共享内存读取从Web服务器下载回来的网页内容。



最后,AsyncResourceHandler类的成员函数OnReadCompleted再向Render进程发送一个类型为ResourceMsg_DataReceived的IPC消息。这个IPC消息告诉Render进程从前面所描述的共享内存的什么位置开始读取多少数据。有了这些数据之后,Render进程就可以构建网页的DOMTree了。



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



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



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

voidResourceDispatcher::DispatchMessage(constIPC::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的实现如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

voidResourceDispatcher::OnSetDataBuffer(intrequest_id,

base::SharedMemoryHandleshm_handle,

intshm_size,

base::ProcessIdrenderer_pid){

......

PendingRequestInforequest_info=GetPendingRequestInfo(request_id);

......



request_info->buffer.reset(

newbase::SharedMemory(shm_handle,true));//readonly



boolok=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对象以一个RequestID为键值保存在ResourceDispatcher类的内部。这个RequestID即为参数request_id描述的RequestID。因此,ResourceDispatcher类的成员函数OnSetDataBuffer可以通过参数request_id获得一个PendingRequestInfo对象。有了这个PendingRequestInfo对象之后,ResourceDispatcher类的成员函数OnSetDataBuffer就根据参数shm_handle描述的句柄创建一个ShareMemory对象,保存在它的成员变量buffer中。



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



ResourceDispatcher类的成员函数OnReceivedData的实现如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

voidResourceDispatcher::OnReceivedData(intrequest_id,

intdata_offset,

intdata_length,

intencoded_data_length){

......

PendingRequestInforequest_info=GetPendingRequestInfo(request_id);

......

if(request_info&&data_length>0){

......

linked_ptrretain_buffer(request_info->buffer);

......



constchardata_start=static_cast(request_info->buffer->memory());

......

constchardata_ptr=data_start+data_offset;

......



//Checkwhetherthisresponsedataiscompliantwithourcross-site

//documentblockingpolicy.Weonlydothisforthefirstpacket.

std::stringalternative_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();



//Whentheresponseisblockedwemayhaveanyalternativedatato

//sendtotherenderer.When|alternative_data|iszero-sized,wedonot

//callpeer''scallback.

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);

//AthreadeddataproviderwilltakecareofitsownACKing,asthe

//datamaybeprocessedlateronanotherthread.

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-SiteDocumentBlockingPolicy决定是否需要阻止下载回来的内容在当前Render进程中加载。关于Chromium的Cross-SiteDocumentBlockingPolicy,可以参考和这两篇文章。



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



如果SiteIsolationPolicy类的静态成员函数ShouldBlockResponse没有阻止下载回来的内容在当前Render进程中加载,或者阻止的同时也提供了AlternativeData,那么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的实现,如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

voidWebURLLoaderImpl::Context::OnReceivedData(constchardata,

intdata_length,

intencoded_data_length){

......



if(ftp_listing_delegate_){

//TheFTPlistingdelegatewillmaketheappropriatecallsto

//client_->didReceiveDataandclient_->didReceiveResponse.

ftp_listing_delegate_->OnReceivedData(data,data_length);

}elseif(multipart_delegate_){

//Themultipartdelegatewillmaketheappropriatecallsto

//client_->didReceiveDataandclient_->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中。



当从Web服务器返回来的网页内容的MIME类型为“text/vnd.chromium.ftp-dir”时,WebURLLoaderImpl::Context类的成员变量ftp_listing_delegate_指向一个FtpDirectoryListingResponseDelegate对象。这时候从Web服务器返回来的网页内容是一些FTP目录,上述FtpDirectoryListingResponseDelegate对象对这些网页内容进行一些排版处理后,再交给WebKit处理,也就是ResourceLoader类的成员变量client_描述的一个ResourceLoader对象处理。



当从Web服务器返回来的网页内容的MIME类型为“multipart/x-mixed-replace”时,WebURLLoaderImpl::Context类的成员变量multipart_delegate_指向一个MultipartResponseDelegate对象。这时候从Web服务器返回来的网页内容包含若干个数据块,每一个数据块都有单独的MIME类型,并且它们之间通过一个BoundaryString。上述MultipartResponseDelegate对象根据BoundaryString解析出每一数据块之后,再交给WebKit处理,也就是ResourceLoader类的成员变量client_描述的一个ResourceLoader对象处理。



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



至此,我们就分析完成Chromium下载指定URL对应的网页内容的过程了。下载回来的网页内容将由WebKit进行处理,也就是由ResourceLoader类的成员函数didReceiveData进行处理。这个处理过程即为网页内容的解析过程,解析后就会得到一棵DOMTree。有了DOMTree之后,接下来就可以对下载回来的网页内容进行渲染了。

献花(0)
+1
(本文系网络学习天...首藏)