Chromium网页URL加载过程分析

转自:http://blog.csdn.net/luoshengyang/article/details/50527574(侵删)

更多阅读,请前往 老罗的Android之旅:Chromium网页加载过程简要介绍和学习计划


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协议请求Web服务器将网页的内容返回来。请求得到响应后,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消息的,如下所示:

[cpp]  view plain  copy
  1. bool RenderFrameImpl::OnMessageReceived(const IPC::Message& msg) {  
  2.   ......  
  3.   
  4.   bool handled = true;  
  5.   IPC_BEGIN_MESSAGE_MAP(RenderFrameImpl, msg)  
  6.     IPC_MESSAGE_HANDLER(FrameMsg_Navigate, OnNavigate)  
  7.     ......  
  8.   IPC_END_MESSAGE_MAP()  
  9.   
  10.   return handled;  
  11. }  
      这个函数定义在文件external/chromium_org/content/renderer/render_frame_impl.cc中。

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

[cpp]  view plain  copy
  1. void RenderFrameImpl::OnNavigate(const FrameMsg_Navigate_Params& params) {  
  2.   ......  
  3.   
  4.   bool is_reload = RenderViewImpl::IsReload(params);  
  5.   ......  
  6.   
  7.   WebFrame* frame = frame_;  
  8.   ......  
  9.   
  10.   if (is_reload) {  
  11.     ......  
  12.   } else if (params.page_state.IsValid()) {  
  13.     ......  
  14.   } else if (!params.base_url_for_data_url.is_empty()) {  
  15.     ......  
  16.   } else {  
  17.     // Navigate to the given URL.  
  18.     WebURLRequest request(params.url);  
  19.     ......  
  20.   
  21.     frame->loadRequest(request);  
  22.   
  23.     ......  
  24.   }  
  25.   
  26.   ......  
  27. }  
       这个函数定义在文件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的实现如下所示:

[cpp]  view plain  copy
  1. void WebLocalFrameImpl::loadRequest(const WebURLRequest& request)  
  2. {  
  3.     ......  
  4.     const ResourceRequest& resourceRequest = request.toResourceRequest();  
  5.   
  6.     if (resourceRequest.url().protocolIs("javascript")) {  
  7.         loadJavaScriptURL(resourceRequest.url());  
  8.         return;  
  9.     }  
  10.   
  11.     frame()->loader().load(FrameLoadRequest(0, resourceRequest));  
  12. }  
       这个函数定义在文件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的实现,如下所示:

[cpp]  view plain  copy
  1. void FrameLoader::load(const FrameLoadRequest& passedRequest)  
  2. {  
  3.     ......  
  4.    
  5.     FrameLoadRequest request(passedRequest);  
  6.     ......  
  7.   
  8.     FrameLoadType newLoadType = determineFrameLoadType(request);  
  9.     NavigationAction action(request.resourceRequest(), newLoadType, request.formState(), request.triggeringEvent());  
  10.     ......  
  11.   
  12.     loadWithNavigationAction(action, newLoadType, request.formState(), request.substituteData(), request.clientRedirect());  
  13.    
  14.     ......  
  15. }  
       这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/loader/FrameLoader.cpp中。

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

       FrameLoader类的成员函数loadWithNavigationAction的实现如下所示:

[cpp]  view plain  copy
  1. void FrameLoader::loadWithNavigationAction(const NavigationAction& action, FrameLoadType type, PassRefPtrWillBeRawPtr<FormState> formState, const SubstituteData& substituteData, ClientRedirectPolicy clientRedirect, const AtomicString& overrideEncoding)  
  2. {  
  3.     ......  
  4.    
  5.     const ResourceRequest& request = action.resourceRequest();  
  6.     ......  
  7.   
  8.     m_policyDocumentLoader = client()->createDocumentLoader(m_frame, request, substituteData.isValid() ? substituteData : defaultSubstituteDataForURL(request.url()));  
  9.     ......  
  10.   
  11.     m_provisionalDocumentLoader = m_policyDocumentLoader.release();  
  12.     ......  
  13.   
  14.     m_provisionalDocumentLoader->startLoadingMainResource();  
  15. }  
       这个函数定义在文件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继承下来的,它的实现如下所示:

[cpp]  view plain  copy
  1. void DocumentLoader::startLoadingMainResource()  
  2. {   
  3.     ......  
  4.   
  5.     FetchRequest cachedResourceRequest(request, FetchInitiatorTypeNames::document, mainResourceLoadOptions);  
  6.     m_mainResource = m_fetcher->fetchMainResource(cachedResourceRequest, m_substituteData);  
  7.     ......  
  8.   
  9.     m_mainResource->addClient(this);   
  10.   
  11.     ......  
  12. }  
       这个函数定义在文件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继承下来的,它的实现如下所示:

[cpp]  view plain  copy
  1. void Resource::addClient(ResourceClient* client)  
  2. {  
  3.     if (addClientToSet(client))  
  4.         didAddClient(client);  
  5. }  
       这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/fetch/Resource.cpp中。

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

[cpp]  view plain  copy
  1. bool Resource::addClientToSet(ResourceClient* client)  
  2. {  
  3.     ......  
  4.   
  5.     m_clients.add(client);  
  6.     return true;  
  7. }  
       这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/fetch/Resource.cpp中。

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

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

[cpp]  view plain  copy
  1. ResourcePtr<RawResource> ResourceFetcher::fetchMainResource(FetchRequest& request, const SubstituteData& substituteData)  
  2. {  
  3.     ......  
  4.     return toRawResource(requestResource(Resource::MainResource, request));  
  5. }  
       这个函数定义在文件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]  view plain  copy
  1. ResourcePtr<Resource> ResourceFetcher::requestResource(Resource::Type type, FetchRequest& request)  
  2. {  
  3.     ......  
  4.   
  5.     KURL url = request.resourceRequest().url();  
  6.     ......  
  7.   
  8.     const RevalidationPolicy policy = determineRevalidationPolicy(type, request.mutableResourceRequest(), request.forPreload(), resource.get(), request.defer(), request.options());  
  9.     switch (policy) {  
  10.     ......  
  11.     case Load:  
  12.         resource = createResourceForLoading(type, request, request.charset());  
  13.         break;  
  14.     .....  
  15.     }  
  16.   
  17.     ......  
  18.   
  19.     if (resourceNeedsLoad(resource.get(), request, policy)) {  
  20.         ......  
  21.    
  22.         if (!m_documentLoader || !m_documentLoader->scheduleArchiveLoad(resource.get(), request.resourceRequest()))  
  23.             resource->load(this, request.options());  
  24.        
  25.         ......  
  26.     }  
  27.   
  28.     ......  
  29.   
  30.     return resource;  
  31. }  
       这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp中。

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

[cpp]  view plain  copy
  1. ResourcePtr<Resource> ResourceFetcher::createResourceForLoading(Resource::Type type, FetchRequest& request, const String& charset)  
  2. {  
  3.     ......  
  4.   
  5.     ResourcePtr<Resource> resource = createResource(type, request.resourceRequest(), charset);  
  6.   
  7.     ......  
  8.     return resource;  
  9. }  
       这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp中。

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

[cpp]  view plain  copy
  1. static Resource* createResource(Resource::Type type, const ResourceRequest& request, const String& charset)  
  2. {  
  3.     switch (type) {  
  4.     ......  
  5.     case Resource::MainResource:  
  6.     case Resource::Raw:  
  7.     case Resource::TextTrack:  
  8.     case Resource::Media:  
  9.         return new RawResource(request, type);  
  10.     ......  
  11.     }  
  12.   
  13.     ......  
  14.     return 0;  
  15. }  
       这个函数定义在文件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]  view plain  copy
  1. void Resource::load(ResourceFetcher* fetcher, const ResourceLoaderOptions& options)  
  2. {  
  3.     ......  
  4.   
  5.     ResourceRequest request(m_resourceRequest);  
  6.     ......  
  7.   
  8.     m_loader = ResourceLoader::create(fetcher, this, request, options);  
  9.     m_loader->start();  
  10. }  
       这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/fetch/Resource.cpp中。

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

[cpp]  view plain  copy
  1. PassRefPtr<ResourceLoader> ResourceLoader::create(ResourceLoaderHost* host, Resource* resource, const ResourceRequest& request, const ResourceLoaderOptions& options)  
  2. {  
  3.     RefPtr<ResourceLoader> loader(adoptRef(new ResourceLoader(host, resource, options)));  
  4.     loader->init(request);  
  5.     return loader.release();  
  6. }  
      这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/fetch/ResourceLoader.cpp中。

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

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

[cpp]  view plain  copy
  1. void ResourceLoader::start()  
  2. {  
  3.     ......  
  4.   
  5.     m_loader = adoptPtr(blink::Platform::current()->createURLLoader());  
  6.     ......  
  7.     blink::WrappedResourceRequest wrappedRequest(m_request);  
  8.     m_loader->loadAsynchronously(wrappedRequest, this);  
  9. }  
       这个函数定义在文件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]  view plain  copy
  1. WebURLLoader* BlinkPlatformImpl::createURLLoader() {  
  2.   return new WebURLLoaderImpl;  
  3. }  
       这个函数定义在文件external/chromium_org/content/child/blink_platform_impl.cc中。

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

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

[cpp]  view plain  copy
  1. void WebURLLoaderImpl::loadAsynchronously(const WebURLRequest& request,  
  2.                                           WebURLLoaderClient* client) {  
  3.   ......  
  4.   
  5.   context_->set_client(client);  
  6.   context_->Start(request, NULL);  
  7. }  
      这个函数定义在文件external/chromium_org/content/child/web_url_loader_impl.cc中。

      从前面的调用过程可以知道,参数client描述的是一个ResourceLoader对象。这个ResourceLoader对象会保存在WebURLLoaderImpl类的成员变量content_描述的一个WebURLLoaderImpl::Context对象的内部。这是通过调用WebURLLoaderImpl::Context类的成员函数set_client实现的,如下所示:

[cpp]  view plain  copy
  1. class WebURLLoaderImpl::Context : public base::RefCounted<Context>,  
  2.                                   public RequestPeer {  
  3.  public:  
  4.   ......  
  5.   
  6.   void set_client(WebURLLoaderClient* client) { client_ = client; }  
  7.   
  8.  private:  
  9.   ......  
  10.   
  11.   WebURLLoaderClient* client_;  
  12.     
  13.   ......  
  14. };  
      这个函数定义在文件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]  view plain  copy
  1. void WebURLLoaderImpl::Context::Start(const WebURLRequest& request,  
  2.                                       SyncLoadResponse* sync_load_response) {  
  3.   ......  
  4.   
  5.   GURL url = request.url();  
  6.   ......  
  7.   
  8.   RequestInfo request_info;  
  9.   ......  
  10.   request_info.url = url;  
  11.   ......  
  12.   bridge_.reset(ChildThread::current()->resource_dispatcher()->CreateBridge(  
  13.       request_info));  
  14.   
  15.   ......  
  16.   
  17.   if (bridge_->Start(this)) {  
  18.     AddRef();  // Balanced in OnCompletedRequest  
  19.   } else {  
  20.     bridge_.reset();  
  21.   }  
  22. }  
       这个函数定义在文件external/chromium_org/content/child/web_url_loader_impl.cc中。

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

[cpp]  view plain  copy
  1. class CONTENT_EXPORT ChildThread  
  2.     : public IPC::Listener,  
  3.       public IPC::Sender,  
  4.       public NON_EXPORTED_BASE(mojo::ServiceProvider) {  
  5.  public:  
  6.   ......  
  7.   
  8.   ResourceDispatcher* resource_dispatcher() const {  
  9.     return resource_dispatcher_.get();  
  10.   }  
  11.   
  12.   ......  
  13.   
  14.  private:  
  15.   ......  
  16.   
  17.   // Handles resource loads for this process.  
  18.   scoped_ptr<ResourceDispatcher> resource_dispatcher_;  
  19.   
  20.   ......  
  21. };  
      这个函数定义在文件external/chromium_org/content/child/child_thread.h中。

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

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

[cpp]  view plain  copy
  1. ResourceLoaderBridge* ResourceDispatcher::CreateBridge(  
  2.     const RequestInfo& request_info) {  
  3.   return new IPCResourceLoaderBridge(this, request_info);  
  4. }  
      这个函数定义在文件external/chromium_org/content/child/resource_dispatcher.cc中。

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

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

[cpp]  view plain  copy
  1. bool IPCResourceLoaderBridge::Start(RequestPeer* peer) {  
  2.   ......  
  3.   
  4.   // generate the request ID, and append it to the message  
  5.   request_id_ = dispatcher_->AddPendingRequest(peer,  
  6.                                                request_.resource_type,  
  7.                                                request_.origin_pid,  
  8.                                                frame_origin_,  
  9.                                                request_.url,  
  10.                                                request_.download_to_file);  
  11.   
  12.   return dispatcher_->message_sender()->Send(  
  13.       new ResourceHostMsg_RequestResource(routing_id_, request_id_, request_));  
  14. }  
      这个函数定义在文件external/chromium_org/content/child/resource_dispatcher.cc中。

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

[cpp]  view plain  copy
  1. int ResourceDispatcher::AddPendingRequest(RequestPeer* callback,  
  2.                                           ResourceType::Type resource_type,  
  3.                                           int origin_pid,  
  4.                                           const GURL& frame_origin,  
  5.                                           const GURL& request_url,  
  6.                                           bool download_to_file) {  
  7.   // Compute a unique request_id for this renderer process.  
  8.   int id = MakeRequestID();  
  9.   pending_requests_[id] = PendingRequestInfo(callback,  
  10.                                              resource_type,  
  11.                                              origin_pid,  
  12.                                              frame_origin,  
  13.                                              request_url,  
  14.                                              download_to_file);  
  15.   return id;  
  16. }  
       这个函数定义在文件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进行接收的,如下所示:

[cpp]  view plain  copy
  1. bool ResourceDispatcherHostImpl::OnMessageReceived(  
  2.     const IPC::Message& message,  
  3.     ResourceMessageFilter* filter) {  
  4.   ......  
  5.   bool handled = true;  
  6.   IPC_BEGIN_MESSAGE_MAP(ResourceDispatcherHostImpl, message)  
  7.     IPC_MESSAGE_HANDLER(ResourceHostMsg_RequestResource, OnRequestResource)  
  8.     ......  
  9.     IPC_MESSAGE_UNHANDLED(handled = false)  
  10.   IPC_END_MESSAGE_MAP()  
  11.   
  12.   ......  
  13. }  
       这个函数定义在文件external/chromium_org/content/browser/loader/resource_dispatcher_host_impl.cc中。

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

[cpp]  view plain  copy
  1. void ResourceDispatcherHostImpl::OnRequestResource(  
  2.     int routing_id,  
  3.     int request_id,  
  4.     const ResourceHostMsg_Request& request_data) {  
  5.   BeginRequest(request_id, request_data, NULL, routing_id);  
  6. }  
       这个函数定义在文件external/chromium_org/content/browser/loader/resource_dispatcher_host_impl.cc中。

       ResourceDispatcherHostImpl类的成员函数OnRequestResource调用另外一个成员函数BeginRequest开始下载参数request_data描述的URL对应的网页内容,后者的实现如下所示:

[cpp]  view plain  copy
  1. void ResourceDispatcherHostImpl::BeginRequest(  
  2.     int request_id,  
  3.     const ResourceHostMsg_Request& request_data,  
  4.     IPC::Message* sync_result,  // only valid for sync  
  5.     int route_id) {  
  6.   ......  
  7.   
  8.   // Construct the request.  
  9.   net::CookieStore* cookie_store =  
  10.       GetContentClient()->browser()->OverrideCookieStoreForRenderProcess(  
  11.           child_id);  
  12.   scoped_ptr<net::URLRequest> new_request;  
  13.   new_request = request_context->CreateRequest(  
  14.       request_data.url, request_data.priority, NULL, cookie_store);  
  15.   ......  
  16.   
  17.   scoped_ptr<ResourceHandler> handler(  
  18.        CreateResourceHandler(  
  19.            new_request.get(),  
  20.            request_data, sync_result, route_id, process_type, child_id,  
  21.            resource_context));  
  22.   
  23.   if (handler)  
  24.     BeginRequestInternal(new_request.Pass(), handler.Pass());  
  25. }  
       这个函数定义在文件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]  view plain  copy
  1. scoped_ptr<ResourceHandler> ResourceDispatcherHostImpl::CreateResourceHandler(  
  2.     net::URLRequest* request,  
  3.     const ResourceHostMsg_Request& request_data,  
  4.     IPC::Message* sync_result,  
  5.     int route_id,  
  6.     int process_type,  
  7.     int child_id,  
  8.     ResourceContext* resource_context) {  
  9.   // Construct the IPC resource handler.  
  10.   scoped_ptr<ResourceHandler> handler;  
  11.   if (sync_result) {  
  12.     ......  
  13.   
  14.     handler.reset(new SyncResourceHandler(request, sync_result, this));  
  15.   } else {  
  16.     handler.reset(new AsyncResourceHandler(request, this));  
  17.   
  18.     // The RedirectToFileResourceHandler depends on being next in the chain.  
  19.     if (request_data.download_to_file) {  
  20.       handler.reset(  
  21.           new RedirectToFileResourceHandler(handler.Pass(), request));  
  22.     }  
  23.   }  
  24.   
  25.   ......  
  26.   
  27.   // Install a CrossSiteResourceHandler for all main frame requests.  This will  
  28.   // let us check whether a transfer is required and pause for the unload  
  29.   // handler either if so or if a cross-process navigation is already under way.  
  30.   bool is_swappable_navigation =  
  31.       request_data.resource_type == ResourceType::MAIN_FRAME;  
  32.   // If we are using --site-per-process, install it for subframes as well.  
  33.   if (!is_swappable_navigation &&  
  34.       CommandLine::ForCurrentProcess()->HasSwitch(switches::kSitePerProcess)) {  
  35.     is_swappable_navigation =  
  36.         request_data.resource_type == ResourceType::SUB_FRAME;  
  37.   }  
  38.   if (is_swappable_navigation && process_type == PROCESS_TYPE_RENDERER)  
  39.     handler.reset(new CrossSiteResourceHandler(handler.Pass(), request));  
  40.   
  41.   // Insert a buffered event handler before the actual one.  
  42.   handler.reset(  
  43.       new BufferedResourceHandler(handler.Pass(), this, request));  
  44.   
  45.   ......  
  46.   
  47.   handler.reset(  
  48.       new ThrottlingResourceHandler(handler.Pass(), request, throttles.Pass()));  
  49.   
  50.   return handler.Pass();  
  51. }  
       这个函数定义在文件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]  view plain  copy
  1. void ResourceDispatcherHostImpl::BeginRequestInternal(  
  2.     scoped_ptr<net::URLRequest> request,  
  3.     scoped_ptr<ResourceHandler> handler) {  
  4.   ......  
  5.   
  6.   ResourceRequestInfoImpl* info =  
  7.       ResourceRequestInfoImpl::ForRequest(request.get());  
  8.   ......  
  9.   
  10.   linked_ptr<ResourceLoader> loader(  
  11.       new ResourceLoader(request.Pass(), handler.Pass(), this));  
  12.   
  13.   .....  
  14.   
  15.   StartLoading(info, loader);  
  16. }  
       这个函数定义在文件external/chromium_org/content/browser/loader/resource_dispatcher_host_impl.cc中。

       ResourceDispatcherHostImpl类的成员函数BeginRequestInternal将参数request描述的URL和参数handler描述的AsyncResourceHandler对象封装在一个ResourceLoader对象后,调用另外一个成员函数StartLoading开始加载参数request描述的URL。

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

[cpp]  view plain  copy
  1. void ResourceDispatcherHostImpl::StartLoading(  
  2.     ResourceRequestInfoImpl* info,  
  3.     const linked_ptr<ResourceLoader>& loader) {  
  4.   ......  
  5.   
  6.   loader->StartRequest();  
  7. }  
      这个函数定义在文件external/chromium_org/content/browser/loader/resource_dispatcher_host_impl.cc中。

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

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

[cpp]  view plain  copy
  1. void ResourceLoader::StartRequest() {  
  2.   ......  
  3.   
  4.   // Give the handler a chance to delay the URLRequest from being started.  
  5.   bool defer_start = false;  
  6.   if (!handler_->OnWillStart(request_->url(), &defer_start)) {  
  7.     Cancel();  
  8.     return;  
  9.   }  
  10.   
  11.   if (defer_start) {  
  12.     deferred_stage_ = DEFERRED_START;  
  13.   } else {  
  14.     StartRequestInternal();  
  15.   }  
  16. }  
       这个函数定义在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]  view plain  copy
  1. void ResourceLoader::StartRequestInternal() {  
  2.   ......  
  3.   
  4.   request_->Start();  
  5.   
  6.   ......  
  7. }  
       这个函数定义在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]  view plain  copy
  1. void ResourceLoader::OnResponseStarted(net::URLRequest* unused) {  
  2.   ......  
  3.   
  4.   if (request_->status().is_success()) {  
  5.     StartReading(false);  // Read the first chunk.  
  6.   }   
  7.   
  8.   ......  
  9. }  
       这个函数定义在external/chromium_org/content/browser/loader/resource_loader.cc中。

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

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

[cpp]  view plain  copy
  1. void ResourceLoader::StartReading(bool is_continuation) {  
  2.   int bytes_read = 0;  
  3.   ReadMore(&bytes_read);  
  4.   
  5.   ......  
  6.   
  7.   if (!is_continuation || bytes_read <= 0) {  
  8.     OnReadCompleted(request_.get(), bytes_read);  
  9.   } else {  
  10.     // Else, trigger OnReadCompleted asynchronously to avoid starving the IO  
  11.     // thread in case the URLRequest can provide data synchronously.  
  12.     base::MessageLoop::current()->PostTask(  
  13.         FROM_HERE,  
  14.         base::Bind(&ResourceLoader::OnReadCompleted,  
  15.                    weak_ptr_factory_.GetWeakPtr(),  
  16.                    request_.get(),  
  17.                    bytes_read));  
  18.   }  
  19. }  
       这个函数定义在external/chromium_org/content/browser/loader/resource_loader.cc中。

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

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

[cpp]  view plain  copy
  1. void ResourceLoader::ReadMore(int* bytes_read) {  
  2.   ......  
  3.   
  4.   scoped_refptr<net::IOBuffer> buf;  
  5.   int buf_size;  
  6.   if (!handler_->OnWillRead(&buf, &buf_size, -1)) {  
  7.     Cancel();  
  8.     return;  
  9.   }  
  10.   
  11.   ......  
  12.   
  13.   request_->Read(buf.get(), buf_size, bytes_read);  
  14.   
  15.   ......  
  16. }  
       这个函数定义在external/chromium_org/content/browser/loader/resource_loader.cc中。

       ResourceLoader类的成员函数ReadMore首先调用成员变量handler_描述的一个AsyncResourceHandler对象的成员函数OnWillRead获取一个Buffer。这个Buffer用来保存从Web服务器返回来的数据。这些数据可以通过调用ResourceLoader类的成员变量reqeust_描述的一个URLRequest对象的成员函数Read获得。 

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

[cpp]  view plain  copy
  1. bool AsyncResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf,  
  2.                                       int* buf_size,  
  3.                                       int min_size) {  
  4.   ......  
  5.   
  6.   if (!EnsureResourceBufferIsInitialized())  
  7.     return false;  
  8.   
  9.   ......  
  10.   char* memory = buffer_->Allocate(&allocation_size_);  
  11.   .....  
  12.   
  13.   *buf = new DependentIOBuffer(buffer_.get(), memory);  
  14.   *buf_size = allocation_size_;  
  15.   
  16.   ......  
  17.   
  18.   return true;  
  19. }  
       这个函数定义在文件external/chromium_org/content/browser/loader/async_resource_handler.cc中。

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

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

[cpp]  view plain  copy
  1. bool AsyncResourceHandler::EnsureResourceBufferIsInitialized() {  
  2.   if (buffer_.get() && buffer_->IsInitialized())  
  3.     return true;  
  4.   
  5.   ......  
  6.   
  7.   buffer_ = new ResourceBuffer();  
  8.   return buffer_->Initialize(kBufferSize,  
  9.                              kMinAllocationSize,  
  10.                              kMaxAllocationSize);  
  11. }  
       这个函数定义在文件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的实现,如下所示:

[cpp]  view plain  copy
  1. void ResourceLoader::OnReadCompleted(net::URLRequest* unused, int bytes_read) {  
  2.   ......  
  3.   
  4.   CompleteRead(bytes_read);  
  5.   
  6.   ......  
  7.   
  8.   if (bytes_read > 0) {  
  9.     StartReading(true);  // Read the next chunk.  
  10.   } else {  
  11.     // URLRequest reported an EOF. Call ResponseCompleted.  
  12.     DCHECK_EQ(0, bytes_read);  
  13.     ResponseCompleted();  
  14.   }  
  15. }  
       这个函数定义在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]  view plain  copy
  1. void ResourceLoader::CompleteRead(int bytes_read) {  
  2.   ......  
  3.   
  4.   bool defer = false;  
  5.   if (!handler_->OnReadCompleted(bytes_read, &defer)) {  
  6.     Cancel();  
  7.   }   
  8.   
  9.   ......  
  10. }  
       这个函数定义在external/chromium_org/content/browser/loader/resource_loader.cc中。

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

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

[cpp]  view plain  copy
  1. bool AsyncResourceHandler::OnReadCompleted(int bytes_read, bool* defer) {  
  2.   ......  
  3.   
  4.   if (!sent_first_data_msg_) {  
  5.     base::SharedMemoryHandle handle;  
  6.     int size;  
  7.     if (!buffer_->ShareToProcess(filter->PeerHandle(), &handle, &size))  
  8.       return false;  
  9.     filter->Send(new ResourceMsg_SetDataBuffer(  
  10.         GetRequestID(), handle, size, filter->peer_pid()));  
  11.     sent_first_data_msg_ = true;  
  12.   }  
  13.   
  14.   int data_offset = buffer_->GetLastAllocationOffset();  
  15.   
  16.   int64_t current_transfer_size = request()->GetTotalReceivedBytes();  
  17.   int encoded_data_length = current_transfer_size - reported_transfer_size_;  
  18.   reported_transfer_size_ = current_transfer_size;  
  19.   
  20.   filter->Send(new ResourceMsg_DataReceived(  
  21.       GetRequestID(), data_offset, bytes_read, encoded_data_length));  
  22.   
  23.   ......  
  24. }  
       这个函数定义在文件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进程就可以构建网页的DOM Tree了。

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

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

[cpp]  view plain  copy
  1. void ResourceDispatcher::DispatchMessage(const IPC::Message& message) {  
  2.   IPC_BEGIN_MESSAGE_MAP(ResourceDispatcher, message)  
  3.     ......  
  4.     IPC_MESSAGE_HANDLER(ResourceMsg_SetDataBuffer, OnSetDataBuffer)  
  5.     IPC_MESSAGE_HANDLER(ResourceMsg_DataReceived, OnReceivedData)  
  6.     ......  
  7.   IPC_END_MESSAGE_MAP()  
  8. }  
       这个函数定义在文件external/chromium_org/content/child/resource_dispatcher.cc中。

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

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

[cpp]  view plain  copy
  1. void ResourceDispatcher::OnSetDataBuffer(int request_id,  
  2.                                          base::SharedMemoryHandle shm_handle,  
  3.                                          int shm_size,  
  4.                                          base::ProcessId renderer_pid) {  
  5.   ......  
  6.   PendingRequestInfo* request_info = GetPendingRequestInfo(request_id);  
  7.   ......  
  8.   
  9.   request_info->buffer.reset(  
  10.       new base::SharedMemory(shm_handle, true));  // read only  
  11.   
  12.   bool ok = request_info->buffer->Map(shm_size);  
  13.   ......  
  14.   
  15.   request_info->buffer_size = shm_size;  
  16. }  

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

[cpp]  view plain  copy
  1. void ResourceDispatcher::OnReceivedData(int request_id,  
  2.                                         int data_offset,  
  3.                                         int data_length,  
  4.                                         int encoded_data_length) {  
  5.   ......  
  6.   PendingRequestInfo* request_info = GetPendingRequestInfo(request_id);  
  7.   ......  
  8.   if (request_info && data_length > 0) {  
  9.     ......  
  10.     linked_ptr<base::SharedMemory> retain_buffer(request_info->buffer);  
  11.     ......  
  12.   
  13.     const char* data_start = static_cast<char*>(request_info->buffer->memory());  
  14.     ......  
  15.     const char* data_ptr = data_start + data_offset;  
  16.     ......  
  17.   
  18.     // Check whether this response data is compliant with our cross-site  
  19.     // document blocking policy. We only do this for the first packet.  
  20.     std::string alternative_data;  
  21.     if (request_info->site_isolation_metadata.get()) {  
  22.       request_info->blocked_response =  
  23.           SiteIsolationPolicy::ShouldBlockResponse(  
  24.               request_info->site_isolation_metadata, data_ptr, data_length,  
  25.               &alternative_data);  
  26.       request_info->site_isolation_metadata.reset();  
  27.   
  28.       // When the response is blocked we may have any alternative data to  
  29.       // send to the renderer. When |alternative_data| is zero-sized, we do not  
  30.       // call peer's callback.  
  31.       if (request_info->blocked_response && !alternative_data.empty()) {  
  32.         data_ptr = alternative_data.data();  
  33.         data_length = alternative_data.size();  
  34.         encoded_data_length = alternative_data.size();  
  35.       }  
  36.     }  
  37.   
  38.     if (!request_info->blocked_response || !alternative_data.empty()) {  
  39.       if (request_info->threaded_data_provider) {  
  40.         request_info->threaded_data_provider->OnReceivedDataOnForegroundThread(  
  41.             data_ptr, data_length, encoded_data_length);  
  42.         // A threaded data provider will take care of its own ACKing, as the  
  43.         // data may be processed later on another thread.  
  44.         send_ack = false;  
  45.       } else {  
  46.         request_info->peer->OnReceivedData(  
  47.             data_ptr, data_length, encoded_data_length);  
  48.       }  
  49.     }  
  50.   
  51.     ......  
  52.   }  
  53.   
  54.   ......  
  55. }  
       这个函数定义在文件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的实现,如下所示:

[cpp]  view plain  copy
  1. void WebURLLoaderImpl::Context::OnReceivedData(const char* data,  
  2.                                                int data_length,  
  3.                                                int encoded_data_length) {  
  4.   ......  
  5.   
  6.   if (ftp_listing_delegate_) {  
  7.     // The FTP listing delegate will make the appropriate calls to  
  8.     // client_->didReceiveData and client_->didReceiveResponse.  
  9.     ftp_listing_delegate_->OnReceivedData(data, data_length);  
  10.   } else if (multipart_delegate_) {  
  11.     // The multipart delegate will make the appropriate calls to  
  12.     // client_->didReceiveData and client_->didReceiveResponse.  
  13.     multipart_delegate_->OnReceivedData(data, data_length, encoded_data_length);  
  14.   } else {  
  15.     client_->didReceiveData(loader_, data, data_length, encoded_data_length);  
  16.   }  
  17. }  

       这个函数定义在文件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类型,并且它们之间通过一个Boundary String。上述MultipartResponseDelegate对象根据Boundary String解析出每一数据块之后,再交给WebKit处理,也就是ResourceLoader类的成员变量client_描述的一个ResourceLoader对象处理。

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

       至此,我们就分析完成Chromium下载指定URL对应的网页内容的过程了。下载回来的网页内容将由WebKit进行处理,也就是由ResourceLoader类的成员函数didReceiveData进行处理。这个处理过程即为网页内容的解析过程,解析后就会得到一棵DOM Tree。有了DOM Tree之后,接下来就可以对下载回来的网页内容进行渲染了。在接下来的一篇文章中,我们再详细分析WebKit根据网页内容生成DOM Tree的过程,敬请关注!更多的信息也可以关注老罗的新浪微博:http://weibo.com/shengyangluo


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值