WebKit资源加载进度

来源:互联网 发布:js 事件触发target 编辑:程序博客网 时间:2024/05/21 11:21

原文转自:http://blog.csdn.net/qq295445028/article/details/8064792

 

 

该文看下在ResourceLoadNotifier::didXXX中对页面加载进度的处理。

首先在ResourceLoadNotifier::didReceiveResponse中,通过Frame找到Page,然后通过Page找到ProgressTracker,然后

ProgressTracker

它负责计算和维护当前接收的进度情况,其中有成员

long longm_totalPageAndResourceBytesToLoad;

//这个记录了页面和资源的总大小,初始时用一个默认值static const int progressItemDefaultEstimatedLength = 1024 * 16;做设置。

long longm_totalBytesReceived;

double m_progressValue;

HashMap<unsigned long,ProgressItem*> m_progressItems;

//每个ResourceLoader有个唯一的identifier,这里对每个ResourceLoader创建一个ProgressItem,并用ResourceLoader的identifier作为key,来设置这个m_progressItemsMap映射。

intm_numProgressTrackedFrames; //该变量用于记录当前有几个Frame正在执行加载的操作。在progressStarted会加一,在progressCompleted时会减一,当它为0时即表示当前没有正在加载的Frame.

RefPtr<Frame>m_originatingProgressFrame; //这个原始的ProgressFrame应该指最顶层的Frame,即Page的主Frame

Page中有成员ProgressTracker,并且是在Page的构造中创建的ProgressTracker成员。

这里要说下Page,Frame,FrameLoader的关系

一个Page对应浏览器的一个Tab页,就是说一个Page对应一个Tab下的完整的html网页

而一个html网页还可以有子Frame用iFrame来标识的,就像猫扑那样的页面,这样一个Page下就可以有多个Frame。其中有一个MainFrame。

一个Frame有一个FrameLoader。

由以上即可知道一个Page有一个ProgressTracker,而每个Frame会对应一个ProgressItem。

ProgressItem

这个类其实是个非常简单的数据结构的类,在ProgressTracker.cpp中被定义的。

long long bytesReceived;

long long estimatedLength; //注意这里只是估计的长度,初始时也是用上面的progressItemDefaultEstimatedLength给赋值的。

就这两个成员。

ProgressTracker的处理

这里主要关注几个函数

progressStarted  //开始加载

incrementProgress //加载中

completeProgress  //完成某个资源的加载

progressCompleted  //一个Frame所有资源都加载完成后,做些结束处理。

接下来挨个看下这几个函数的情况。

ProgressTracker::progressStarted

在开始执行加载时被调用,看下调用栈:

#0 WebCore::ProgressTracker::progressStarted

#1WebCore::FrameLoader::prepareForLoadStart

#2WebCore::DocumentLoader::prepareForLoadStart

#3WebCore::FrameLoader::continueLoadAfterWillSubmitForm

#4WebCore::FrameLoader::continueLoadAfterNavigationPolicy

#5WebCore::FrameLoader::callContinueLoadAfterNavigationPolicy

#6WebCore::PolicyCallback::call

#7WebCore::PolicyChecker::continueAfterNavigationPolicy

#8 android::FrameLoaderClientAndroid::dispatchDecidePolicyForNavigationAction

#9 WebCore::PolicyChecker::checkNavigationPolicy

#10WebCore::FrameLoader::loadWithDocumentLoader

#11WebCore::FrameLoader::load

#12WebCore::FrameLoader::load

#13WebCore::FrameLoader::load

#14 LoadUrl

#15 dvmPlatformInvoke

可见在LoadUrl的调用链上发生的,由FrameLoader::prepareForLoadStart调用,即在真正开始发出http请求时调用。

这里会判断下当前是否没有正在加载的Frame,即判断m_numProgressTrackedFrames的值是否为0。或者判断当前的Frame是否是m_originatingProgressFrame,即是否是主Frame。如果是这两种的其中一种,会执行ProgressTracker::reset的操作,将所有成员清成初始值,然后设置一下配置,初始配置包括对m_originatingProgressFrame的设置和对m_progressValue的设置,m_progressValue的初始值是什么?初始没有进度的,这里设置成一个常量值,当前是0.1。之后会对m_numProgressTrackedFrames的值加一,表示已经开始有一个Frame执行加载的操作了。

在ProgressTracker的处理中大多数都是在自身做一些进度方面的计算后或者配置后,执FrameLoaderClientAndroid类的通知接口。典型的接口名为XXXNotification或者didXXX的形式。即通知和回调的接口。

那么ProgressTracker如何找到FrameLoaderClientAndroid呢,有二种途径。

第一种是通过参数传入的Frame。通过Frame找到FrameLoader,进一步找到FrameLoaderClient,在android平台上时FrameLoaderClientAndroid。

第二种通过成员m_originatingProgressFrame,这个成员本身是一个Frame的类型,按上述找到FrameLoaderClientAndroid。

FrameLoaderClientAndroid

该类是平台相关的FrameLoaderClient继承类,它有个重要的成员WebFrame,该WebFrame在android下是external/webkit/Source/WebKit/android/jni/WebCoreFrameBridge.h中定义的。这个WebFrame又有个成员JavaBrowserFrame,这个就对应了java层的BrowserFrame类,通过它可以和BrowserFrame通过jni相互调用。那么WebFrame其实就是对JavaBrowserFrame的包装,对外可以直接把WebFrame看成跟java层BrowserFrame的对应了。这样WebFrame提供的通知和回调接口,就可以发给java层的BrowserFrame了。

由上可知在ProgressTracker的处理中通过Frame找到了FrameLoaderClientAndroid,调用FrameLoaderClientAndroid的接口。FrameLoaderClientAndroid的接口中可以通过成员WebFrame来实现对java层的BrowserFrame的操作。这样流程就从平台无关的WebCore的ProgressTracker中通知到了平台相关的android的java层中了。

知道了这个流程后,会发现ProgressTracker的很多处理都是这样,就是自己做了计算后,通过上述流程调用到java层中的BrowserFrame中去。

ProgressTracker::incrementProgress

该函数有两种重载形式,一种是void ProgressTracker::incrementProgress(unsigned long identifier,const ResourceResponse& response)另一种是voidProgressTracker::incrementProgress(unsigned long identifier, const char*, intlength)

ProgressTracker::incrementProgress(unsignedlong identifier, const ResourceResponse& response)

当一个Frame接收到http的Response后会执行该重载函数,看下调用栈:

#0 WebCore::ProgressTracker::incrementProgress(this=0x11f6f18, identifier=44, response=...)

#1WebCore::ResourceLoadNotifier::didReceiveResponse

#2WebCore::ResourceLoader::didReceiveResponse

#3WebCore::MainResourceLoader::continueAfterContentPolicy

#4 WebCore::MainResourceLoader::continueAfterContentPolicy

#5WebCore::MainResourceLoader::callContinueAfterContentPolicy

#6 WebCore::PolicyCallback::call

#7WebCore::PolicyChecker::continueAfterContentPolicy

#8android::FrameLoaderClientAndroid::dispatchDecidePolicyForResponse

#9WebCore::PolicyChecker::checkContentPolicy

#10WebCore::MainResourceLoader::didReceiveResponse

#11WebCore::ResourceLoader::didReceiveResponse

#12android::WebUrlLoaderClient::didReceiveResponse

#13 DispatchToMethod

#14 RunnableMethod

#15 RunTask

首先从response中获取资源总长度信息作为该资源的估计长度,如果没有则用一个默认值来设置。然后把这个估计长度加入到m_totalPageAndResourceBytesToLoad,可见这个成员的值是不靠谱的值。

接下来会查询下m_progressItems中有没有跟参数传入的identifier对应的ProgressItem,如果有就获取它并重置下值,如果没有就新建一个ProgressItem并加入到m_progressItems映射中。注意每个ResourceLoader有一个identifier。则每个ResourceLoader对应一个ProgressItem。ProgressItem可以表明这个ResourceLoader估计要下载的数据量和已经下载的数据量。在这个处理中主要就是配置好该ResourceLoader对应的ProgressItem,然后等待接收数据了。

ProgressTracker::incrementProgress(unsignedlong identifier, const char*, int length)

当一个Frame接收到数据后会执行该重载函数,看下调用栈:

#0 WebCore::ProgressTracker::incrementProgress

#1WebCore::ResourceLoadNotifier::didReceiveData

#2WebCore::ResourceLoader::didReceiveData

#3WebCore::MainResourceLoader::didReceiveData

#4WebCore::ResourceLoader::didReceiveData

#5android::WebUrlLoaderClient::didReceiveData

#6 DispatchToMethod

#7 RunnableMethod

ProgressTracker::incrementProgres的参数有个unsignedlong identifie,用于告诉ProgressTracker是哪个ResourceLoader执行的,也即找到m_progressItems中对应的ProgressItem。

在ResourceLoadNotifier::didReceiveData中会将得到的数据长度作为参数传入。

ProgressTracker会利用这个长度来做一些计算,ProgressTracker的m_totalBytesReceived和ProgressItem的bytesReceived也会相应做增加,把新的数据长度加进来。

在ProgressTracker::incrementProgress中会通过Frame找到Frameloader,进一步找到FrameLoaderClient,这里是FrameLoaderClientAndroid。之后调用FrameLoaderClientAndroid::postProgressEstimateChangedNotification,在这里可以找到WebFrame,它是平台相关的类,可以在这里通过JNI调用到java层的代码处,在WebFrame中调用了WebFrame::SetProgress,把新的进度设置给java层。跟上述ProgressTracker::progressStarted的过程类似。

ProgressTracker::completeProgress

当一个Frame完成加载数据时,会执行该函数。看下调用栈:

#0 WebCore::ProgressTracker::completeProgress

#1WebCore::ResourceLoadNotifier::didFinishLoad

#2WebCore::ResourceLoader::didFinishLoadingOnePart

#3WebCore::ResourceLoader::didFinishLoading

#4WebCore::MainResourceLoader::didFinishLoading

#5WebCore::ResourceLoader::didFinishLoading

#6android::WebUrlLoaderClient::didFinishLoading

#7 DispatchToMethod

#8 RunnableMethod

#9 RunTask

该函数也会受到一个identifier的参数。这样通过m_progressItems找到该ResourceLoader对应的ProgressItem。

调整下估计值的情况,调整后,m_totalPageAndResourceBytesToLoad总算是靠谱了。

之后移除并delete掉这个ResourceLoader对应的ProgressItem,因为它已经没用了。

ProgressTracker::progressCompleted

一个Frame所有资源都加载完成后,做些结束处理。看下调用栈:

#0 WebCore::ProgressTracker::progressCompleted

#1WebCore::FrameLoader::checkLoadCompleteForThisFrame

#2WebCore::FrameLoader::recursiveCheckLoadComplete

#3WebCore::FrameLoader::checkLoadComplete

#4WebCore::DocumentLoader::removeSubresourceLoader

#5WebCore::SubresourceLoader::didFinishLoading

#6WebCore::ResourceLoader::didFinishLoading

#7android::WebUrlLoaderClient::didFinishLoading

#8 DispatchToMethod

#9 RunnableMethod

#10 RunTask

该Frame的所有资源都加载完了,则m_numProgressTrackedFrames计数减一。

之后做下判断m_numProgressTrackedFrames为0时则调用ProgressTracker::finalProgressComplete做最后的处理。ProgressTracker::finalProgressComplete函数的处理中又会调用ProgressTracker::reset做复位的操作,之后还是通知FrameLoaderClientAndroid做些回调处理。而ProgressTracker::progressCompleted的处理中也会发通知给FrameLoaderClientAndroid。

至此就完成了整个流程。

在以上的所有过程中FrameLoaderClientAndroid都可以通过m_frame->page()->progress()找到ProgressTracker,然后就可以读取ProgressTracker中计算的进度值情况了。

以上的处理中FrameLoaderClientAndroid主要应用的接口是

virtual void postProgressStartedNotification();

virtual voidpostProgressEstimateChangedNotification();

virtual voidpostProgressFinishedNotification();

原创粉丝点击