1、Chromium网页Render Object Tree创建过程分析Chromium网页Render Object Tree创建过程分析在前面一文中,我们分析了网页DOM Tree的创建过程。网页DOM Tree创建完成之后,WebKit会根据它的内容创建一个Render Object Tree。Render Object Tree是和网页渲染有关的一个Tree。这意味着只有在DOM Tree中需要渲染的节点才会在Render Object Tree中有对应节点。本文接下来就分析网页Render Object Tree的创建过程。从前面一文可以知道,每一个HTML标签在DOM Tree中都有一
2、个对应的HTMLElement节点。相应地,在DOM Tree中每一个需要渲染的HTMLElement节点在Render Object Tree中都有一个对应的RenderObject节点,如图1所示:从图1还可以看到,Render Object Tree创建完成之后,WebKit还会继续根据它的内容创建一个Render Layer Tree和一个Graphics Layer Tree。本文主要关注Render Object Tree的创建过程。 从前面一文还可以知道,DOM Tree是在网页内容的下载过程中创建的。一旦网页内容下载完成,DOM Tree就创建完成了。网页的Render Obj
3、ect Tree与DOM Tree不一样,它是在网页内容下载完成之后才开始创建的。因此,接下来我们就从网页内容下载完成时开始分析网页的Render Object Tree的创建过程。 从前面一文可以知道,WebKit是通过Browser进程下载网页内容的。Browser进程一方面通过Net模块中的URLRequest类去Web服务器请求网页内容,另一方面又通过Content模块中的ResourceLoader类的成员函数OnReadCompleted不断地获得URLRequest类请求回来的网页内容,如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片voi
4、d 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
5、/content/browser/loader/resource_loader.cc中。 参数bytes_read表示当前这次从URLRequest类中读取回来的网页内容的长度。当这个长度值等于0的时候,就表示所有的网页内容已经读取完毕。这时候ResourceLoader类的成员函数OnReadCompleted就会调用另外一个成员函数ResponseCompleted进行下一步处理。 ResourceLoader类的成员函数ResponseCompleted的实现如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片void ResourceLoader:R
6、esponseCompleted() . handler_-OnResponseCompleted(request_-status(), security_info, &defer); . 这个函数定义在文件external/chromium_org/content/browser/loader/resource_loader.cc中。 在前面一文中,我们假设ResourceLoader类的成员变量handler_指向的是一个AsyncResourceHandler对象。ResourceLoader类的成员函数ResponseCompleted调用这个AsyncResourceHandler对
7、象的成员函数OnResponseCompleted进行下一步处理。 AsyncResourceHandler类的成员函数OnResponseCompleted的实现如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片void AsyncResourceHandler:OnResponseCompleted( const net:URLRequestStatus& status, const std:string& security_info, bool* defer) const ResourceRequestInfoImpl* info = GetRequ
8、estInfo(); . ResourceMsg_RequestCompleteData request_complete_data; request_complete_data.error_code = error_code; request_complete_data.was_ignored_by_handler = was_ignored_by_handler; request_complete_data.exists_in_cache = request()-response_info().was_cached; request_complete_data.security_info
9、= security_info; request_complete_pletion_time = TimeTicks:Now(); request_complete_data.encoded_data_length = request()-GetTotalReceivedBytes(); info-filter()-Send( new ResourceMsg_RequestComplete(GetRequestID(), request_complete_data); 这个函数定义在文件external/chromium_org/content/browser/loader/async_res
10、ource_handler.cc中。 AsyncResourceHandler类的成员函数OnResponseCompleted所做的事情是向Render进程发送一个类型为ResourceMsg_RequestComplete的IPC消息,用来通知Render进程它所请求的网页内容已下载完毕。 Render进程是通过ResourceDispatcher类的成员函数DispatchMessage接收类型为ResourceMsg_RequestComplete的IPC消息的,如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片void ResourceDispa
11、tcher:DispatchMessage(const IPC:Message& message) IPC_BEGIN_MESSAGE_MAP(ResourceDispatcher, message) . IPC_MESSAGE_HANDLER(ResourceMsg_RequestComplete, OnRequestComplete) IPC_END_MESSAGE_MAP() 这个函数定义在文件external/chromium_org/content/child/resource_dispatcher.cc中。 从这里可以看到,ResourceDispatcher类的成员函数Dispa
12、tchMessage将类型为ResourceMsg_RequestComplete的IPC消息分发给另外一个成员函数OnRequestComplete处理。 ResourceDispatcher类的成员函数OnRequestComplete的实现如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片void ResourceDispatcher:OnRequestComplete( int request_id, const ResourceMsg_RequestCompleteData& request_complete_data) . PendingReq
13、uestInfo* request_info = GetPendingRequestInfo(request_id); . RequestPeer* peer = request_info-peer; . peer-OnCompletedRequest(request_complete_data.error_code, request_complete_data.was_ignored_by_handler, request_complete_data.exists_in_cache, request_complete_data.security_info, renderer_completi
14、on_time, request_complete_data.encoded_data_length); 这个函数定义在文件external/chromium_org/content/child/resource_dispatcher.cc中。 从前面一文可以知道,Render进程在请求Browser进程下载指定URL对应的网页内容之前,会创建一个PendingRequestInfo对象。这个PendingRequestInfo对象以一个Request ID为键值保存在ResourceDispatcher类的内部。这个Request ID即为参数request_id描述的Request ID。
15、因此,ResourceDispatcher类的成员函数OnRequestComplete可以通过参数request_id获得一个PendingRequestInfo对象。有了这个PendingRequestInfo对象之后,ResourceDispatcher类的成员函数OnSetDataBuffer再通过它的成员变量peer获得一个WebURLLoaderImpl:Context对象,并且调用它的成员函数OnCompletedRequest通知它下载网页内容的请求已完成。 WebURLLoaderImpl:Context类的成员函数OnCompletedRequest的实现如下所示:cpp
16、view plain copy 在CODE上查看代码片派生到我的代码片void WebURLLoaderImpl:Context:OnCompletedRequest( int error_code, bool was_ignored_by_handler, bool stale_copy_in_cache, const std:string& security_info, const base:TimeTicks& completion_time, int64 total_transfer_size) . if (client_) if (error_code != net:OK) clie
17、nt_-didFail(loader_, CreateError(request_.url(), stale_copy_in_cache, error_code); else client_-didFinishLoading( loader_, (completion_time - TimeTicks().InSecondsF(), total_transfer_size); . 这个函数定义在文件external/chromium_org/content/child/web_url_loader_impl.cc中。 从前面一文可以知道,WebURLLoaderImpl:Context类的成员
18、变量client_指向的是WebKit模块中的一个ResourceLoader对象。在成功下载完成网页内容的情况下,WebURLLoaderImpl:Context类的成员函数OnCompletedRequest调用这个ResourceLoader对象的成员函数didFinishLoading通知WebKit结束解析网页内容。 ResourceLoader类的成员函数didFinishLoading的实现如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片void ResourceLoader:didFinishLoading(blink:WebURLLoa
19、der*, double finishTime, int64 encodedDataLength) . m_resource-finish(finishTime); . 这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/fetch/ResourceLoader.cpp中。 ResourceLoader类的成员变量m_resource描述的是一个RawResource对象。这个RawResource对象的创建过程可以参考前面一文。ResourceLoader类的成员函数didFinishLoading调用这个RawReso
20、urce对象的成员函数finish结束加载网页内容。 RawResource类的成员函数finish是从父类Resource继承下来的,它的实现如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片void Resource:finish(double finishTime) . finishOnePart(); . 这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/fetch/Resource.cpp中。 Resource类的成员函数finish调用另外一个成员函数finishOneP
21、art结束加载网页的内容。注意Resource类的成员函数finishOnePart的命名。有前面一文中,我们提到,当网页内容的MIME类型为“multipart/x-mixed-replace”时,下载回来网页内容实际是包含多个部分的,每一个部分都有着自己的MIME类型。每一个部分下载完成时,都会调用Resource类的成员函数finishOnePart进行处理。为了统一接口,对于MIME类型不是“multipart/x-mixed-replace”的网页内容而言,下载回来的网页内容也是当作一个部分进行整体处理。 Resource类的成员函数finishOnePart的实现如下所示:cpp
22、view plain copy 在CODE上查看代码片派生到我的代码片void Resource:finishOnePart() . checkNotify(); 这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/fetch/Resource.cpp中。 Resource类的成员函数finishOnePart调用另外一个成员函数checkNotify通知当前正在前处理的Resource对象的Client,它们所关注的资源,也就是网页内容,已经下载完成了。 Resource类的成员函数checkNotify的实现如下所示:
23、cpp view plain copy 在CODE上查看代码片派生到我的代码片void Resource:checkNotify() . ResourceClientWalker w(m_clients); while (ResourceClient* c = w.next() c-notifyFinished(this); 这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/fetch/Resource.cpp中。 从前面一文可以知道,在Resource类的成员变量m_clients中,保存有一个DocumentLoad
24、er对象。这个DocumentLoader对象是从ResourceClient类继承下来的,它负责创建和加载网页的文档对象。Resource类的成员函数checkNotify会调用这个DocumentLoader对象的成员函数notifyFinished通知它要加载的网页的内容已经下载完成了。 DocumentLoader类的成员函数notifyFinished的实现如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片void DocumentLoader:notifyFinished(Resource* resource) . if (!m_mainRes
25、ource-errorOccurred() & !m_mainResource-wasCanceled() finishedLoading(m_mainResource-loadFinishTime(); return; . 这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/loader/DocumentLoader.cpp中。 DocumentLoader类的成员变量m_mainResource指向的是一个RawResource对象。这个RawResource对象和前面分析的ResourceLoader类的成员变量m_
26、resource指向的是同一个RawResource对象。这个RawResource对象代表正在请求下载的网页内容。在网页内容成功下载完成的情况下,DocumentLoader类的成员函数notifyFinished就会调用另外一个成员函数finishedLoading进行结束处理。 DocumentLoader类的成员函数finishedLoading的实现如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片void DocumentLoader:finishedLoading(double finishTime) . endWriting(m_write
27、r.get(); . 这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/loader/DocumentLoader.cpp中。 从前面一文可以知道,DocumentLoader类的成员变量m_writer指向的是一个DocumentWriter对象。DocumentLoader类的成员函数finishedLoading调用另外一个成员函数endWriting告诉这个DocumentWriter对象结束对正在加载的网页内容的解析。 DocumentLoader类的成员函数endWriting的实现如下所示:cpp view
28、 plain copy 在CODE上查看代码片派生到我的代码片void DocumentLoader:endWriting(DocumentWriter* writer) . m_writer-end(); . 这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/loader/DocumentLoader.cpp中。 DocumentLoader类的成员函数endWriting调用上述DocumentWriter对象的成员函数end结束对正在加载的网页内容的解析。 DocumentWriter类的成员函数end的实现如下所
29、示:cpp view plain copy 在CODE上查看代码片派生到我的代码片void DocumentWriter:end() . m_parser-finish(); . 这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/loader/DocumentWriter.cpp中。 从前面一文可以知道,DocumentWriter类的成员变量m_parser指向的是一个HTMLDocumentParser对象。DocumentWriter类的成员函数end调用这个HTMLDocumentParser对象的成员函数fin
30、ish结束对正在加载的网页内容的解析。 HTMLDocumentParser类的成员函数finish的实现如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片void HTMLDocumentParser:finish() . attemptToEnd(); 这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.cpp中。 HTMLDocumentParser类的成员函数finish调用另外一个成员函数attemptToEnd结束对正在加载的网页内容的解析。 HTMLDocumentParser类的成员函数attemptToEnd的实现如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片v