原文地址: http://peirenlei.iteye.com/blog/1718569

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

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

一、类结构及接口

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

如上是 Loader 和 NetWork 之间的类关系图

ResourceHandleClient 是ResourceHandle 的客户,它定义一系列虚函数,这些虚函数是 ResouceHandle 的回调,继承类实现这些接口,ResourceHandleClient更多的继承关系如下图:

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

一般情况下,在发起网络请求前调用,可以设置特定的 Http头部,比如 user agent 等,在重定向请求的时候,也会自动调用

virtual void willSendRequest(ResourceHandle*, ResourceRequest&, const ResourceResponse& /*redirectResponse*/) { }

上传数据的时候,在 TCP wrtie 事件的时候,向对方发送数据的时候调用, loader 可以根据这个回调显示上传进度。

virtual void didSendData(ResourceHandle*, unsigned long long /*bytesSent*/, unsigned long long /*totalBytesToBeSent*/) { }

收到第一个响应包,此时至少 http 的部分头部已经解析(如status code ), loader 根据响应的头部信息判断请求是否成功等

virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&) { }

收到 HTTP 响应数据,类似 tcp 的 read 事件,来 http 响应数据了, Network 的设计机制是来一段数据上传一段数据

virtual void didReceiveData(ResourceHandle*, const char*, int, int /*encodedDataLength*/) { }

加载完成,数据来齐

virtual void didFinishLoading(ResourceHandle*, double /*finishTime*/) { }

加载失败

virtual void didFail(ResourceHandle*, const ResourceError&) { }

要求用户鉴权

virtual 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 页面时,一个正常的加载流程。

三、主资源加载过程

. DocumentLoader 调用 MainResourceLoader::load 向 loader 发起请求
. 调用 MainResourceLoader::loadNow
. 调用 MainResourceLoader::willSendRequest
. 调用 ResourceLoader::willSendRequest, 将 callback 通过 ResourceNotifier 传导给 FrameLoaderClient 。 Client 可以在回调中操作 ResourceRequest ,比如设置请求头部。
. 调用 PolicyChecker::checkNavigationPolicy 过滤掉重复请求等
. loader 调用 ResourceHandle::create 向 Network 发起加载请求
. 收到第一个 HTTP 响应数据包 ,Network 回调MainResourceLoader::didReceiveResponse ,主要处理 HTTP 头部。
. 调用 PolicyChecker:: checkContentPolicy, 并最终通过 FrameLoaderClient 的dispatchDecidePolicyForMIMEType 判断是否为下载请求(存在 "Content-Disposition"http 头部)
. 调用 MainResourceLoader::continueAfterContentPolicy ,根据ResourceResponse 检测是否发生错误。
. 调用 ResourceLoader::didReceiveResponse ,将 callback 通过 ResourceNotifier传导给 FrameLoaderClient 。
. 收到 HTTP 体部数据,调用 MainResourceLoader::didReceiveData
. 调用 ResourceLoader::didReceiveData ,将 callback 通过 ResourceNotifier 传导给 FrameLoaderClient
. 调用 MainResourceLoader::addData
. 调用 DocumentLoader::receivedData
. 调用 DocumentLoader::commitLoad
. 调用 FrameLoader::commitProvisionalLoad , FrameLoader 从 provisional 状态跃迁到 Committed 状态
. 调用 FrameLoaderClientQt::committedLoad
. 调用 DocumentLoader::commitData ,启动 Writer 对象来处理数据(DocumentWriter::setEncoding , DocumentWriter::addData )
. 调用 DocumentWriter::addData
. 调用 DocumentParser::appendByte
. 调用 DecodedDataDocumentParser::appendBytes 对文本编码进行解码
. 调用 HTMLDocumentParser::append ,进行 HTML 解析
. 数据来齐,调用 MainResourceLoader::didFinishLoading
. 调用 FrameLoader::finishedLoading
. 调用 DocumentLoader::finishedLoading
. 调用 FrameLoader::finishedLoadingDocument ,启动 writer 对象接收剩余数据,重复 - 进行解析
. 调用 DocumentWriter::end 结束接收数据(调用 Document::finishParsing )
. 调用 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 类根据申请的资源类型派生出不同的子类

对于资源的加载客户端CacheResourceClient及其子类的关系如图

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

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

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

.   解析 html 页面的时候,解析到 img 标签,调用 HTMLImageElement::create创建 HTMLImageElement 对象,该对象包含 HTMLImageLoader 对象m_imageLoader
. 解析到 img 的 href 属性,调用ImageLoader::updateFromElementIgnoringPreviousError
. 调用 ImageLoader::updateFromElement
. 调用 CachedResourceLoader::requestImage
. 调用 CachedResourceLoader::requestResource( 根据缓存的情况确定是否可以从缓存获取,或者需要 revalidate ,或者需要直接从网络获取 )
. 调用 CachedResourceLoader::loadResource
. 根据 Resource 的类型调用 createResource 创建对应的 CachedResource
. 调用 MemoryCache::add 在 cache 中查找是否有对应的 cache 条目,如果没有创建之
. 调用 CachedImage::load
. 调用 CachedResource::load
. 调用 CachedResourceLoader::load
. 调用 CachedResourceRequest::load
. 创建 CachedResourceRequest 对象,它将作为 SubresourceLoader 的 client
. 调用 ResourceLoaderScheduler::scheduleSubresourceLoad
. 调用 SubresourceLoader::create
. ResourceLoadScheduler::requestTimerFired
. 调用 ResourceLoader::start
. 调用 ResourceHandle::create 发起请求
. 收到 HTTP 响应头部,调用 ResourceLoader::didReceiveResponse
. 调用 SubresourceLoader::didiReceiveResponse
. 调用 CachedResourceRequest::didReceiveResponse 处理响应头部,特别是同缓存相关的头部,比如 的 status code
. 调用 ResourceLoader::didReceiveResponse
. 收到体部数据,调用 ResourceLoader::didReceiveData
. 调用 SubresourceLoader::didReceiveData
. 调用 ResourceLoader::didReceiveData
. 调用 ResourceLoader::addData 将数据存储到 SharedBuffer 里面
. 调用 CachedResourceRequest::didReceiveData
. 数据来齐 , 调用 ResourceLoader::didFinishLoading
. 调用 SubresourceLoader::didFinishLoading
. 调用 CachedResourceRequest::didFinishLoading
. 调用 CachedResource::finish
. 调用 CachedResourceLoader::loadDone
. 调用 CachedImage::data ,创建对应的 Image 对象,解码

对于文档的解析是通过DocumentParse及其子类类完成的,看一下子类继承关系

WebKit 源码分析 -- loader的更多相关文章

  1. jQuery实现DOM加载方法源码分析

    传统的判断dom加载的方法 使用 dom0级 onload事件来进行触发所有浏览器都支持在最初是很流行的写法 我们都熟悉这种写法: window.onload=function(){ ... }  但 ...

  2. spring源码分析之spring-core总结篇

    1.spring-core概览 spring-core是spring框架的基石,它为spring框架提供了基础的支持. spring-core从源码上看,分为6个package,分别是asm,cgli ...

  3. Spring源码分析——资源访问利器Resource之实现类分析

    今天来分析Spring的资源接口Resource的各个实现类.关于它的接口和抽象类,参见上一篇博文——Spring源码分析——资源访问利器Resource之接口和抽象类分析 一.文件系统资源 File ...

  4. Tomcat源码分析

    前言: 本文是我阅读了TOMCAT源码后的一些心得. 主要是讲解TOMCAT的系统框架, 以及启动流程.若有错漏之处,敬请批评指教! 建议: 毕竟TOMCAT的框架还是比较复杂的, 单是从文字上理解, ...

  5. angular源码分析:angular的整个加载流程

    在前面,我们讲了angular的目录结构.JQLite以及依赖注入的实现,在这一期中我们将重点分析angular的整个框架的加载流程. 一.从源代码的编译顺序开始 下面是我们在目录结构哪一期理出的an ...

  6. angular源码分析:angular的源代码目录结构说明

    一.读源码,是选择"编译合并后"的呢还是"编译前的"呢? 有朋友说,读angular源码,直接看编译后的,多好,不用管模块间的关系,从上往下读就好了.但是在我看 ...

  7. jQuery.buildFragment源码分析以及在构造jQuery对象的作用

    这个方法在jQuery源码中比较靠后的位置出现,主要用于两处.1是构造jQuery对象的时候使用 2.是为DOM操作提供底层支持,这也就是为什么先学习它的原因.之前的随笔已经分析过jQuery的构造函 ...

  8. Solr初始化源码分析-Solr初始化与启动

    用solr做项目已经有一年有余,但都是使用层面,只是利用solr现有机制,修改参数,然后监控调优,从没有对solr进行源码级别的研究.但是,最近手头的一个项目,让我感觉必须把solrn内部原理和扩展机 ...

  9. Backbone.js源码分析(珍藏版)

    源码分析珍藏,方便下次阅读! // Backbone.js 0.9.2 // (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc. // Backbone ...

随机推荐

  1. 决策树&随机森林

    参考链接: https://www.bilibili.com/video/av26086646/?p=8 <统计学习方法> 一.决策树算法: 1.训练阶段(决策树学习),也就是说:怎么样构 ...

  2. Facebook 被指收集用户数据:通过照片和文本

    北京时间5月25日消息,在加利福尼亚州进行的对Facebook泄露用户信息一案中,法院对Facebook提起一项新的诉讼,指控该公司通过App收集了用户及他们朋友的信息. 上周向加利福尼亚州圣马特奥市 ...

  3. FPGA软硬协同设计学习笔记及基础知识(一)

    一.FPGA软件硬件协同定义: 软件:一般以软件语言来描述,类似ARM处理器的嵌入式设计.硬件定义如FPGA,里面资源有限但可重配置性有许多优点,新的有动态可充配置技术. Xilinx开发了部分动态可 ...

  4. java中stream部分笔记

    Stream流表面上看起来与集合类似,允许你转换和检索数据.然而,两者却有显著的不同1.流不存储元素.它们存储在底层的集合或者按需生成2.流操作不改变他们的源数据.例如filter方法不会从一个新流中 ...

  5. SAP Odata実行命令(2)

    前言 $ skiptokenは.アプリケーションに送信されるエントリ数を制限するために使用されます. 膨大な数のエントリが要求された場合.これはパフォーマンスの向上にも役立ちます.次のリンクがアプリケ ...

  6. Java设计模式(10)——结构型模式之代理模式(Proxy)

    一.概述 概念 UML简图 实际使用的场景示例 如果那个对象是一个是很大的图片,需要花费很长时间才能显示出来,那么当这个图片包含在文档中时,使用编辑器或浏览器打开这个文档,打开文档必须很迅速,不能等待 ...

  7. 20145209刘一阳 《网络对抗》Exp7 网络欺诈技术防范

    20145209刘一阳 <网络对抗>Exp7 网络欺诈技术防范 一.应用SET工具建立冒名网站 要让冒名网站在别的主机上也能看到,需要开启本机的Apache服务,并且要将Apache服务的 ...

  8. 在同一台机器上启动多个tomcat服务(转)

    转载:https://blog.csdn.net/wangxy799/article/details/53957770 1.案例:配置一台机上配置三个Tomcat 2.方法1:[只用修改第一个以外To ...

  9. MFC 中的设计模式分析

    MFC 中的设计模式分析 最近在学习设计模式,突然想到MFC里面其实也包含有设计模式的原理,于是分析了一下,做一个笔记,网上也找了一些资料,在此一并感谢. 创建型模式 单例模式(Singleton P ...

  10. ASP.NET中Gridview一些技巧

    ASP.NET中Gridview一些技巧 一.后台覆盖掉Gridview中自动填充的值 我们可以再Gridview中的事件触发的过程中修改其中的值,而这些值将会在具体的运行过程中覆盖掉那些自动属性.这 ...