Chinaunix首页 | 论坛 | 博客
  • 博客访问: 304857
  • 博文数量: 174
  • 博客积分: 3061
  • 博客等级: 中校
  • 技术积分: 1740
  • 用 户 组: 普通用户
  • 注册时间: 2006-05-04 22:43
文章分类

全部博文(174)

文章存档

2011年(54)

2010年(14)

2009年(30)

2008年(26)

2007年(27)

2006年(23)

我的朋友

分类: WINDOWS

2011-07-21 15:54:36

最近由于工作需要需要使用html5离线部署程序。

一开始用几个比较简单的文件进行尝试的时候,发现到也没有什么问题,但是随着manifest文件越来越大之后,发现问题越来越多,这儿有几点需要说明


1. html5的离线部署它会根据加载的html文件的manifest文件去检索清单文件,如果清单文件和本地的不一致,则将清单文件中的每一项排入队列下载

2. html5的离线部署将appcachehost和documentloader关联到一起,意味着documentloader销毁了,则则appcachehost也将销毁

3. qtwebkit的代码显示appcache中如果还有资源在下载,但是这个时候documentloader又销毁了,则整个离线部署的cache机制将无效,因为m_pendingMasterResourceLoaders  这个变量在 void ApplicationCacheGroup::disassociateDocumentLoader(DocumentLoader* loader)时候并没有销毁。

这个问题,在我使用的14.x的chrome也照样存在,另外通过代码的阅读,实际上这个问题,还是应该通过判断applicationcache的通知来彻底解决。

-----------------------------------------------------------------

今天由于工作需要,需要进一步理解html5 offline 的机制以及原理,这儿还是采用qtwebkit的代码,

同时选择qtdemo browser作为浏览器,访问我自己的一个测试页面。

 

   

       

   

   

       

   

 

 

CACHE MANIFEST

#The file is only for index.html

CACHE:

 

#NETWORK:

#*

 

 

另外注意在apache server上打开mimetypeappcache类型的选择。

这个也参看w3chtml5草案文档, http://dev.w3.org/html5/spec/offline.html#offline

 

另外webkit 官网有一张图,表示加载页面的几个内部对象关系

http://www.webkit.org/blog/1188/how-webkit-loads-a-web-page/

 

当然还需要在qt的代码中,打开offlinecache的选项。

 

 

我们先简单做一个静态分析,即webkit中这部分代码。。

 

它位于webkit\WebCore\loader\appcache

 

 

Manifestparser

这个比较简单,就是清单文件的解析对象。

 

DOMApplicationCache

??

 

ApplicationCacheStorage

应该是底层处理sqlite存储的一个类。

 

ApplicationCacheResource

 

ApplicationCacheHost

应该是每一个html文件,就会作为就会作为一个cachehost,是否有manifest属性貌似不影响这个对象创建,最多就是selectwithoutmanifest而已。

 

ApplicationCacheGroup

同属于同一个manifest文件的资源属于一个group.

 

ApplicationCache

貌似在内存在中管理一个组的cache

 

 

我们先运行browser,然后设置好断点,由于这是首次运行,因此访问的page都没有cache.

 

1.      DocumentLoader 创建并且管理了ApplicationCachehost

        

1)   applicationcachehost 记录了documentloader,但是不涉及lifttime

 

2.      尝试用documentloader加载

 

Documentloader会尝试询问applicationcachehost是否这个资源你哪儿有?

 

1)      Applicationcachehost 先判断下qtwebkit是否开启了离线部署支持.

2)      然后通过ApplicationCacheGroup的一个静态函数 ; m_mainResourceApplicationCache = ApplicationCacheGroup::cacheForMainRequest(request, m_documentLoader); 要求Cache 此请求

3)      CacheGroup 内部判断是否是http请求且是http get

4)      通过一个friend的全局函数cacheStorage, 尝试将request访问的url进行cache,cachestorage ()这个函数调用,实际上返回一个静态的局部变量,表示applicationcachestorage

5)      Cachestorage对象,尝试创建数据库文件,默认文件名为applicationcache.db; 如果文件不存在则创建文件,并且创建一系列的表。

6)      由于是首次加载,所以并不存在这样的cachegroup,也没有这样的资源,所以

m_mainResourceApplicationCache = ApplicationCacheGroup::cacheForMainRequest(request, m_documentLoader);

这个呼叫最终得到NULL; 如果有cache的实际上,将直接尝试从db中获取。

         7 由于cache中没,所以程序继续执行,就像没有开启离线部署那样

 

3.      webserver上拿到了整个indexHtml,这个从httpanalyzer上也能够看到

 

 

1) 这个时候发现在收到数据的时候,applicationcachehost又得到了一次机会执行。

这个时候,实际上是检查这个httpstatus

if (response.httpStatusCode() / 100 == 4 || response.httpStatusCode() / 100 == 5)

        if (scheduleLoadFallbackResourceFromApplicationCache(resourceLoader))

            return true;

return false;

 

由于本次是第一次执行,所以retcode=200,因此返回false,从代码来看,如果retcode=404 之类的,将尝试从本地cache读取。

 

 

2) Webkit将给appcache一次机会自行处理收到的数据

 

注意这儿的char* 实际上就是收到的html数据。

 

4.       到了这儿,documentloder销毁,对应的applicationcachehost 销毁.这个文件的下载过程结束了,也没有appcache啥实质性的事情。由于是第一次运行,cache中没有,而index.htmlmanifest还没有得到解析,因此这个最初的第一阶段就是如此。

 

5.      继续执行,这个时候到了webkit解析html文件的时候,来看

 

 

会发现webkit在处理 标签的时候会特别检查下是否有manifest atrr这个属性。 像我们的程序就是有的,所以如上图,就让documentloaderappcachehost对象设置一个manifest。继续看。。

1)      Selectcachewithmanifest 执行一系列判断,比如离线是否开启,manifesturl是否合法,请求是否是http http-get,清单文件所在的html文件的scheme是否和manifest文件的一致,最后在本次运行中发现manifest 文件需要从网络上下载.

2)      通过cachestorage(静态变量)findOrCreateCacheGroup 来检查是否已经存在这样一个group了,按照我的理解html5貌似以manifest文件来区分group.即一个manifest构成了一个group.

3)      接下来将发现实际上group并没有创建,所以创建一个

group = new ApplicationCacheGroup(manifestURL);

m_cacheHostSet.add(urlHostHash(manifestURL));

4)      设置appcachehostcandidategroup为这个group

5)      接下来将需要下载的资源数量+1

group->m_pendingMasterResourceLoaders.add(documentLoader);

group->m_downloadingPendingMasterResourceLoadersCount++;

6)      为这个manifest获取创建一个request

  m_manifestHandle = createResourceHandle(m_manifestURL, m_newestCache ? m_newestCache->manifestResource() : 0); 这个过程创建的一个resourcehandle然后这个handler实际上将groupthis指针作为ResourceHandleClient* 传了过去,所以当manifest下载完成后,会得到didfinishload的回调通知。 这个resourcehandlenetworkreplyclienthandler关联的做法很多地方都有

7)      继续运行,在获取manifest文件前,收到了

 

应该是表示index.html已经先于manifest文件加载。

 

8)      继续运行,这儿会收到一些内部事件护理,这儿暂且不表。。。

9)      继续运行,终于从web server上获取到了manifest 文件。

 

接下来applicationcachegroupdidreceiveresponse得到机会执行。

这个函数的逻辑是先判断是否是manifest的下载,像我们这个例子这儿确实是manifest的处理。如果不是下面有一个比较复杂的处理先不说。

 

10)   didReceiveManifestResponse 得到执行,大致进行了一些检查,如果retcode =404 or 410 则直接返回;= 304 直接返回; != 2xx , 或者类型不是cache-manifest 也返回; 否则创建一个resource, 由于这儿的retcode=200,所以

m_manifestResource = ApplicationCacheResource::create(m_manifestHandle->request().url(), response,                                                           ApplicationCacheResource::Manifest);

        

11) 接下来void ApplicationCacheGroup::didReceiveData(ResourceHandle* handle, const char* data, int length, int)

得到机会执行,m_manifestResource->data()->append(data, length); 会填充数据

             这儿为什么是append我有点搞不明白??

         12didFinishLoading 得到执行didFinishLoadingManifest

1) 本例子,将判定当前的操作是不是upgradetemp,因此直接解析manifest文件,解析后会得到masterentryfallbackentrynetworkentry

            我这儿还是不太理解fallback entry是干啥的?

             2) 接下来将m_pendingMasterResourceLoaders 和新创建的applicationcache关联;

             3) 接下来将expliciturlfallbackurlnetworkentry 加入队列

         13) startLoadingEntry 得到执行

             1) 依次将所有pendingloader 执行finishload操作;

2) cachegroup检查到pendingentry = 0,所以对传入的每个loader所在的url执行cache操作; void ApplicationCache::addResource(PassRefPtr resource)

3) m_downloadingPendingMasterResourceLoadersCount--;

    checkIfLoadIsComplete();

    检查是否还有没有下载完成的masterresource,结果=0

4)   最后通过appcachestorage 写入数据库

 

  到了这儿,一个最最简单的,首次加载完成了。

 

1: 调试时候其实发现documentloader创建了2次,并且其中一次很快被析构了,实际上是qwebview首次初始化时候会用about:blank 进行初始化所以,这个documentloader是为它创建的,对本次调试没实际价值。

 

2: 本实验中index.html 下载完成后finishedLoadingMainResource 并不将其放入cache,而是要等待manifest下载完成。

 

 

实验2 : 将manifest文件修改为

CACHE MANIFEST

#The file is only for index.html

CACHE:

index.html

 

#NETWORK:

#*

 

即增加了index.html作为manster entry in explicit mode, index,html本身又是直接ref manifest的文件,根据html5的草案,认为这个行为可做可不做。

 

但是从我对qtwebkitdebug来看,这个行为做了至少效率会降低。

 

 

如上图,在首次加载时候,index.html已经被下载了,然后解析manifest之后,又发起下载manifest,结果解析manifest时候发现又有一个master entry就是index.html结果只能再次发起请求要求得到index.html,所以抓包就如上图所示般index.html被搞了2.

 

 

 

实验3: 在实验2产生的数据基础上,关闭apache服务器,观察cache如何生效

1.   前期的过程和实验1比较类似到了MainResourceLoader::load ,从而触发ApplicationCacheHost::maybeLoadMainResource,注意这儿时候触发的request urlindex.html

2.   ApplicationCacheGroup::cacheForMainRequest 在检查这个index.html时候发现数据库已经存在,且存在对应的groupid,因此直接执行loadcache,即在加载这个index.html的时候已经在这个cachegroup的所有资源从db中加载进来了

3.   ApplicationCacheHost::maybeLoadMainResource 继续检查是否cache中存在request,填充substitude数据结构,然后执行loadnow

4.   Loadnow检查到substitude 数据结构已经合法存在,因此不再从net上获取数据;而是从本地启动一个定时器模拟貌似已经从网上获取到了数据MainResourceLoader::handleDataLoadNow被呼叫

5.   模拟触发didReceiveResponse,这个就像从网上拿到数据之后的触发一样

6.   解析到manifest标签,不同的是,这个标签还是尝试从web上获取,但是会带上if-modified--since

7.   由于我们关闭了apache,所以didfailed被触发 cachegroup::  cacheUpdateFailed();  被触发

8.   Cachegroup::stopload

9.   ApplicationCacheGroup::checkIfLoadIsComplete 进入failed处理

10. 到了这儿index.html也加载成功了,完成了离线部署

 

 

实验4: 在实验3的基础上将apache打开,但是不更新manifest

基本同实验3,无非是appcache文件从服务端拿到。

 

 

实验5: 解决一个问题,即index.html立刻跳转的问题.

function onLoad()

        {         

     window.location = "";

        

        }

加上上面的代码,将发现在实验5的基础上cachedb文件将无法生成了,这个就是我们目前面临的问题。

 

 

通过对代码的调试也基本上能够发现,void ApplicationCacheGroup::disassociateDocumentLoader(DocumentLoader* loader) 这个函数在window.location切换的时候由于documentloader析构而导致被调用,而这个调用又导致了m_pendingMasterResourceLoaders 不一致,即到不了0.(这个时候还有下载项在处理)

 

 

基本认为有两个思路

1.   修改webkit的代码,比如pendingMasterResourceLoaders-1 disassociateDocumentLoader的时候

2.   使用window.applicationcahce对象的几个事件,在第一个global页面内就对此进行处理,只要有更新且正在更新则要等待更新结束

 

阅读(1432) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~