当前位置: 代码迷 >> Web前端 >> WebKit 内核源代码分析 ( 4 )
  详细解决方案

WebKit 内核源代码分析 ( 4 )

热度:953   发布时间:2012-11-20 09:55:43.0
WebKit 内核源代码分析 ( 四 )

摘要:本文介绍?WebCore??Loader?模块是如何加载资源的,分主资源和派生资源分析?loader?模块的类关系。

关键词:?WebKit,Loader,Network,ResouceLoader,SubresourceLoader

一、类结构及接口

Loader?模块是?Network?模块的客户。?Network?模块提供指定资源的获取和上传功能,获取的资源可能来自网络、本地文件或者缓存。对不同?HTTP?实现的适配会在?Network?层完成,所以?Loader?接触到的基本上是同?OS??HTTP?实现无关的Network?层接口。

?


?

如上是?Loader??NetWork?之间的类关系图,?ResourceHandleClient?ResourceHandle?的客户,它定义一系列虚函数,这些虚函数是?ResouceHandle?的回调,继承类实现这些接口。

ResouceHandleClient?的接口同网络传输过程息息相关,一般为某一个网络事件对应的回调。下面是其中的一些接口。

//?一般情况下,在发起网络请求前调用,可以设置特定的?Http

头部,比如?user agent?等,在重定向请求的时候,也会自动调

void willSendRequest(ResourceHandle*, ResourceRequest&, const

ResourceResponse&)

//?上传数据的时候,在?TCP wrtie?事件的时候,向对方发送数据的

时候调用,?loader?可以根据这个回调显示上传进度。

void didSendData(ResourceHandle*, unsigned long long

/*bytesSent*/, unsigned long long /*totalBytesToBeSent*/)

//?收到第一个响应包,此时至少?http?的部分头部已经解析(如

status code?),?loader?根据响应的头部信息判断请求是否成功

等。

void didReceiveResponse(ResourceHandle*,

const???ResourceResponse&)

//?收到?HTTP?响应数据,类似?tcp??read?事件,来?http?响应数据

了,?Network?的设计机制是来一段数据上传一段数据。

void didReceiveData(ResourceHandle*, const char*, int,

??int /*lengthReceived*/)

????//?加载完成,数据来齐。

void didFinishLoading(ResourceHandle*, double /*finishTime*/)

//?加载失败

void didFail(ResourceHandle*, const ResourceError&)

//?要求用户鉴权

void didReceiveAuthenticationChallenge(ResourceHandle*,

const AuthenticationChallenge&)

WebCore?把要加载的资源分成两类,一类是主资源,比如?HTML?页面,或者下载项,一类是派生资源,比如?HTML?页面中内嵌的图片或者脚本链接。这两类资源对于回调的处理有很大的不同,比如,同样是下载失败,主资源可能需要向用户报错,派生资源比如页面中的一张图下载失败,可能就是图不显示或者显示代替说明文字而已,不向用户报错。因此有了?MainResourceLoader??SubresourceLoader?之分。它们的公共基类?ResourceLoader?则完成一些两种资源下载都需要完成的操作,比如通过回调将加载进程告知上层应用。

ResourceLoader?通过?ResourceNotifier?类将回调传导到?FrameLoaderClient?类。


????????

主资源的加载是立刻发起的,而派生资源则可能会为了优化网络,在队列中等待(?这里的立刻发起是?loader?层面的,不是?Network?层面的?)??ResourceScheduler?这个类就是用来管理资源加载的调度。主要调度对象就是派生资源,会根据?host?来影响资源加载的先后顺序。

主资源和派生资源的加载还有一个区别,主资源目前是没有缓存的,而派生资源是有缓存机制的。这里的缓存指的是?Resouce Cache?,用于保存原始数据(比如CSS??JS?等),以及解码过的图片数据,通过?Resource Cache?可以节省网络请求和图片解码的时候。不同于?Page Cache??Page Cache?存的是?DOM?树和?Render?树的数据结构,用来在前进后退的时候快速显示页面。

二、加载流程

????下图是加载?html?页面时,一个正常的加载流程。

?

三、主资源加载过程

1.????????DocumentLoader?调用?MainResourceLoader::load??loader?发起请求

2.????????调用?MainResourceLoader::loadNow

3.????????调用?MainResourceLoader::willSendRequest

4.????????调用?ResourceLoader::willSendRequest,??callback?通过?ResourceNotifier?传导给?FrameLoaderClient??Client?可以在回调中操作?ResourceRequest?,比如设置请求头部。

5.????????调用?PolicyChecker::checkNavigationPolicy?过滤掉重复请求等

6.????????loader?调用?ResourceHandle::create??Network?发起加载请求

7.????????收到第一个?HTTP?响应数据包?,Network?回调MainResourceLoader::didReceiveResponse?,主要处理?HTTP?头部。

8.????????调用?PolicyChecker::?checkContentPolicy,?并最终通过?FrameLoaderClient?dispatchDecidePolicyForMIMEType?判断是否为下载请求(存在?"Content-Disposition"http?头部)

9.????????调用?MainResourceLoader::continueAfterContentPolicy?,根据ResourceResponse?检测是否发生错误。

10.???调用?ResourceLoader::didReceiveResponse?,将?callback?通过?ResourceNotifier传导给?FrameLoaderClient?

11.???收到?HTTP?体部数据,调用?MainResourceLoader::didReceiveData

12.???调用?ResourceLoader::didReceiveData?,将?callback?通过?ResourceNotifier?传导给?FrameLoaderClient

13.???调用?MainResourceLoader::addData

14.???调用?DocumentLoader::receivedData

15.???调用?DocumentLoader::commitLoad

16.???调用?FrameLoader::commitProvisionalLoad??FrameLoader??provisional?状态跃迁到?Committed?状态

17.???调用?FrameLoaderClientQt::committedLoad

18.???调用?DocumentLoader::commitData?,启动?Writer?对象来处理数据(DocumentWriter::setEncoding??DocumentWriter::addData?

19.???调用?DocumentWriter::addData

20.???调用?DocumentParser::appendByte

21.???调用?DecodedDataDocumentParser::appendBytes?对文本编码进行解码

22.???调用?HTMLDocumentParser::append?,进行?HTML?解析

23.???数据来齐,调用?MainResourceLoader::didFinishLoading

24.???调用?FrameLoader::finishedLoading

25.???调用?DocumentLoader::finishedLoading

26.???调用?FrameLoader::finishedLoadingDocument?,启动?writer?对象接收剩余数据,重复?19-22?进行解析

27.???调用?DocumentWriter::end?结束接收数据(调用?Document::finishParsing?

28.???调用?HTMLDocumentParser::finish

四、派生资源加载流程

??在派生资源的加载中,?SubresourceLoader?更多起到的是一个转发的作用,通过它的?client??SubresourceLoaderClient?类)来完成操作。


?

?

?

???各个加载阶段的处理在?SubresourceLoaderClient?的派生类CachedResourceRequest,Loader,IconLoader?中完成。?Client?会创建?SubresourceLoader

请求发起阶段,?ResourceLoadScheduler?负责对?SubresourceLoader?进行调度。


???Document?类会创建?CachedResourceLoader?类的对象?m_cachedResourceLoader,这个类?(?对象?)?提供了对?Document?的派生资源的访问接口?requestImage?requestCSSStyleSheet??requestUserCSSStyleSheet??requestScript??requestFont?requestXSLStyleSheet??requestLinkPrefetch?。为了实现这些接口,CachedResourceLoader?需要创建?CachedResourceRequest?对象来发起请求。

一般情况下,一个?Document?拥有一个?CachedResourceLoader?类实例。

MemoryCache?类则对提供缓存条目的管理,可以方便地进行?add??remove?,缓存淘汰等。具体的缓存条目则是通过?CachedResource?类存储,?MemoryCache?类维护了一个?HashMap?存储所有缓存条目。

HashMap <String,CachedResource> m_resources;

CachedResourceRequest?依赖于?CachedResource,??CacheResourceRequest?的构造函数中,会传入?CachedResource?对象作为参数。?CachedResource?既存储响应体部,也存储同?cache?相关的头部。在发起请求前,会检查是否有?cache??validator?,在收到响应的时候,则需要更新对应的头部。?CachedResource?类实现了?RFC2616?中的缓存一节。实际上?CachedResource?类真正完成了同网络的通信。?CachedResource?类根据申请的资源类型派生出不同的子类。



??????CachedResource?类的使用者必须是?CachedResourceClient,?在这个类中维护了CachedResourceClient?类的集合?m_clients?。每一个?Client?通过?addClient?removeClient?将自己加入到该类的?Client?集合中。?CachedResourceClientWalker?则提供了?CachedResouceClient?的一个遍历接口。当数据来齐的时候,?CachedResource?类会通过?CachedResouceClient::notifyFinished?接口通知使用者。

下图是?Image?元素对应的几个类关系。



?

?

下面以?image?为例分析其加载过程

1.????????解析?html?页面的时候,解析到?img?标签,调用?HTMLImageElement::create创建?HTMLImageElement?对象,该对象包含?HTMLImageLoader?对象m_imageLoader

2.????????解析到?img??href?属性,调用ImageLoader::updateFromElementIgnoringPreviousError

3.????????调用?ImageLoader::updateFromElement

4.????????调用?CachedResourceLoader::requestImage

5.????????调用?CachedResourceLoader::requestResource(?根据缓存的情况确定是否可以从缓存获取,或者需要?revalidate?,或者需要直接从网络获取?)

6.????????调用?CachedResourceLoader::loadResource

7.????????根据?Resource?的类型调用?createResource?创建对应的?CachedResource

8.????????调用?MemoryCache::add??cache?中查找是否有对应的?cache?条目,如果没有创建之

9.????????调用?CachedImage::load

10.???调用?CachedResource::load

11.???调用?CachedResourceLoader::load

12.???调用?CachedResourceRequest::load

13.???创建?CachedResourceRequest?对象,它将作为?SubresourceLoader??client

14.???调用?ResourceLoaderScheduler::scheduleSubresourceLoad

15.???调用?SubresourceLoader::create

16.???ResourceLoadScheduler::requestTimerFired

17.???调用?ResourceLoader::start

18.???调用?ResourceHandle::create?发起请求

19.???收到?HTTP?响应头部,调用?ResourceLoader::didReceiveResponse

20.???调用?SubresourceLoader::didiReceiveResponse

21.???调用?CachedResourceRequest::didReceiveResponse?处理响应头部,特别是同缓存相关的头部,比如?304??status code

22.???调用?ResourceLoader::didReceiveResponse

23.???收到体部数据,调用?ResourceLoader::didReceiveData

24.???调用?SubresourceLoader::didReceiveData

25.???调用?ResourceLoader::didReceiveData

26.???调用?ResourceLoader::addData?将数据存储到?SharedBuffer?里面

27.???调用?CachedResourceRequest::didReceiveData

28.???数据来齐?,?调用?ResourceLoader::didFinishLoading

29.???调用?SubresourceLoader::didFinishLoading

30.???调用?CachedResourceRequest::didFinishLoading

31.???调用?CachedResource::finish

32.???调用?CachedResourceLoader::loadDone

33.???调用?CachedImage::data?,创建对应的?Image?对象,解码

  相关解决方案