今天关注webkit中dom树是怎么构建的,HTML是怎么分析的。 1、dom相关的代码在webcore中dom目录下,有很多类,比较重要的是 Document.h、DocumentFragment.h、DocumentParser.h DocumentParser是一个基类感觉好像没干什么事,只是传入了一个开始parse的状态,在DocumentFragment中有parseHTML,好像开始了真正的parse操作; 2、HTML的parse操作,从DocumentFragment中的parseHTML开始,相关的parser放置在WebCore/html/parser下,具体是由HTMLDocumentParser来完成的。 DocumentFragment::parseHTML() -->HTMLDocumentParser::parseDocumentFragment() -->HTMLDocuemntParser::insert() { 设置好输入流 调用pumpTokenlizerIfPossible准备建树 -->HTMLDocumentParser::pumpTokenizerIfPossible() -->HTMLDocumentParser::pumpTokenizer() { while() { HTMLTokenizer::nextToken(stream, HTMLToken::m_token) HTMLTreeBuilder::constructTreeFromToken(m_token) { 将HTMLToken转成HTMLTreeBuilder::constructTree; 再调用建树函数constructTree (m_token); -->HTMLTreeBuilder:: processToken () { 根据token的类型分别调用: HTMLTreeBuilder::ASSERT_NOT_REACHED() HTMLTreeBuilder::processDoctypeToken() HTMLTreeBuilder::processStartTag() HTMLTreeBuilder::processEndTag() HTMLTreeBuilder::processComment() HTMLTreeBuilder::processCharacter() HTMLTreeBuilder::processEndOfFile() 完成树的构建 } } } } } 整个Dom树的构建过程非常清晰,其中一些比较重要的类,包括:HTMLDocumentParser完成parse的整体流程,协调HTMLInputStream、HTMLTokenlizer、HTMLTreeBuilder的工作,其中: 1)HTMLInputStream负责HTML数据的输入; 2)HTMLTokenizer根据输入的数据流把HTML进行tokenize; 3)HTMLTreeBuilder根据输入的token进行建树、完成树的构建; HTMLTokenizer的实现就是一个大的状态机,根据input的数据,不断变换着内部的状态,完成tokenization的过程,吐出一个个的token。 树的构建过程是通过HTMLTreeBuilder和HTMLContructionSite等类来完成的,Dom树的构建过程是比较复杂的,但原理是比较清晰的,根据HTML文件的嵌套顺序,通过一个栈(HTMLElementStack m_openElements)来保存当前打开的节点标签。假设一段HTML的数据如下: <div> <p> <a> </a> </p> <br> </br> </div> 我们知道div节点有两个子节点,p和br,而p节点又有一个子节点a,具体处理过程简单描述如下: 假设当前栈为空; 首先处理<div>,遇到<div>时,调用processStartTag(),m_openElements中首先压入div; 继续处理,遇到<p>标签,调用processStartTag(),此时栈顶的div就是p节点的父节点,建立好p和div的连接,在加入节点p; 继续处理,遇到<a>标签,调用processStartTag(),此时栈定节点是p,当前节点是a,那么a的父节点是p,建立好父子关系,并将a压入栈; 继续处理,遇到</a>标签,调用processEndTag(),弹出栈定元素a(代码中使用“弹出直到标签a”一提高容错性,因为有可能会有没封闭的节点,导致栈混乱,使用弹出知道标签xxx,可以将没有封闭的标签带来的问题,限制在局部范围内,防止扩散); 继续处理,遇到</p>标签,调用processEndTag(),弹出p 继续处理,遇到<br>标签,调用processStartTag(),此时栈顶的div就是br节点的父节点,建立好br和div的连接,在加入节点br; 继续处理,遇到</br>标签,调用processEndTag(),弹出br 继续处理,遇到</div>标签,调用processEndTag(),弹出div DOM树建立完毕。 原理虽然如此,但因为浏览器为了用户的方便,允许网页中有各种的错误,浏览器必须能够尽可能的显示出用户的数据,这就给DOM树的构建增加了很多复杂的处理,具体来说,DOM的数构建是由一个状态机来控制和容错的,比如在processStartTag函数中,根据当前构建过程的状态和获得的HTML token来决定下一步的动作,构建过程的状态包括:InitialMode, BeforeHTMLMode, BeforeHeadMode, InHeadMode, AfterHeadMode, InBodyMode, InTableMode, InCapationMode, InColumnGroupMode,...等等,如何根据这些状态和输入的token执行动作,在参考文献[1]中有全面的描述。经过这样的过程Dom树应就算建立完成了。 |
|
来自: wusiqi111 > 《DOM tree》