分享

WebKit之DOM树构建流程分析

 wusiqi111 2019-05-10

今天关注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树应就算建立完成了。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多