WebKit 内核源码分析 (五)
本文分析WebKit中html的解析过程,DOM节点树的建立。
关键词:WebKit,html解析,html tree construction,WebCore,
DOM节点树,dlmu2001
1. HTML解析模型
图1 HTML解析模型图
上图是HTML解析模型图,HTML解析分成Tokeniser和Tree Construction两个步骤,在”WebKit中的html词法分析”
(http://blog.csdn.net/dlmu2001/archive/2010/11/09/5998130.aspx)一文中,我们已经对Tokeniser这一步进行了分析,本文的目标是Tree Construction这一步。
Tree Construction输入是token流,输出是DOM节点树。
2. DOM树
HTML DOM定义了一套标准来将html文档结构化,它定义了表示和修改文档所需的对象、这些对象的行为和属性以及对象之间的关系,可以把它理解为页面上数据和结构的一个树形表示。
Node是DOM模型中的基础类,它可以分成13类(见NodeType),在HTML解析中,最常见的是Document,Element,Text三类。
l Document是文档树的根节点,在HTML文档中,他派生为HTMLDocument。
l 在文档中,所有的标签转化为Element类,一般它有标签名,并根据标签名继承为特定的子类。
l Element之间的原始文本转化成Text类。
以一个简单的html页面为例:
<html>
<head>
<title>test</title>
</head>
<body>
<h1>hl1</h1>
<h2>hl2</h2>
<h3>hl3</h3>
</body>
</html>
经过解析后的节点树如下(忽略换行符):
图2 HTML DOM节点树示例
如果没有忽略换行符,则每个换行符就是一个Value为”\n”的Text节点。
3. Tree Construction原理
将图二中的节点树以WebKit中的类具体化(同样忽略换行符)。
图3 Webkit HTML DOM节点树示例
看到这里,你是不是觉得仿佛看到了一个呼之欲出的Tree Construction轮廓?是的,最简化的情况就是这样,根据输入的token,创建出相应的Element派生类,然后添加到DOM树中合适的位置,这就是Tree Construction干的事情。当然,添加到合适的位置,这个需要一系列复杂的规则,另外,WebKit将Render树的创建也放到了Tree Construction阶段中来,再加上CSS,Javascript,所以,这就是你看到的复杂的代码。
放出两个函数原型,热热身,培养培养感情。
- PassRefPtr<Element> HTMLConstructionSite::createHTMLElement(AtomicHTMLToken& token);
- void HTMLConstructionSite::insertHTMLElement(AtomicHTMLToken& token);<span style="color:#595959;FONT-SIZE: 12pt"> </span>
Tree Construction流程由一个状态“Insertion Mode”进行控制,它影响token的处理以及是否支持CDATA部分,HTML5中给出了详细的规则(http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#the-insertion-mode)。它也控制了在特定状态下能够处理的token,比如在head里面,再出现head标签,显然是不应该处理的。
4. 开放元素堆栈
为了维护即将解析的标签同已解析的标签之间的关系(此时即将解析的标签还没有加入到DOM树中),引入了开放元素堆栈m_openElements,初始状态下,这个堆栈是空的,它是向下增长的,所以最上面的节点是最早加入到堆栈中的,在html文档中,最上面的节点就是html元素,最底部的节点就是最新加入到堆栈中的。Tree Builder的时候,每碰到一个StartTag的token,就会往m_opnElements中压栈,碰到EndTag的token,则出栈。像Character这样的token,则不需要进行压栈出栈的动作,只有可以包含子节点的tag,才做压栈出栈的动作。Html5的文档中对开放元素堆栈也有说明,http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#the-stack-of-open-elements。
对于正在解析的token,除了根节点html,它必然是堆栈底部元素(m_openElements.top())的子节点,所以在形成DOM树的时候,就可以通过ContainerNode::parserAddChild这样的接口加入到DOM节点树中。
除了正常的堆栈和压栈,对于html,head,body元素,栈结构(HTMLElementStack)中有专门的成员m_htmlElement,m_headElement,m_bodyElement记录,主要是用于检错纠错处理。
在本文的html范例中,当解析到<h2>hl2</h2>的hl2这个character的token的时候,它的开放元素堆栈如下,HTMLHeadingElement是堆栈的top,所以它是hl2这个Text节点的parent。
图4 开放元素堆栈示例
此时的DOM节点树如下:
图5 Webkit DOM节点数示例
5. 元素的创建
HTMLElementFactory类提供了元素的创建方法createHTMLElement。传入为对应的标签名,所属的document,所属的form(如果属于form),在parser的时候,最后一个参数为true。
- PassRefPtr<HTMLElement> HTMLElementFactory::createHTMLElement(const QualifiedName& qName, Document* document, HTMLFormElement* formElement, bool createdByParser);
在HTMLElementFactory中,通过一个Hash Map将tag name和对应的元素构造函数对应起来(gFunctionMap)。tag一般对应一个派生于HTMLElement的类。如下是HTMLHeadingElement的类层次结构图。
图6 HTMLHeadingElement类层次图
6. 其它
HTMLConstructionSite::attach中的attach一词,地瓜理解主要是attach到DOM节点数上,当然,它同时调用了Element::attach,Element类的attach主要是attach到Render树上,它会创建对应该Element的RendrObject。
除了m_openElements,HTMLConstructionSite同时维护了Format 元素列表m_activeFormattingElements,Formating元素就是那些格式化标签,包括a,b,big,code,em,font,I,fot,I,nobr,s,small,strike,strong,tt,u。为了处理这些Formatting元素的嵌套关系(此时它们可能不是父子关系,而是平级,不加入到m_openElements),HTML5引入了这个列表(http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#list-of-active-formatting-elements)。
使用gdb调试的童子,可以运行Tools/gdb/webkit.py脚本,在print结构体的时候得到易于理解的表示,还可以打印出节点树,具体参考http://trac.webkit.org/wiki/GDB。
WebKit 内核源码分析 (五)相关推荐
- WebKit 内核源码分析 (三) Page
浏览器的请求一般是以页面请求为单位,当用户通过网址栏输入一个url,浏览器就开始一个页面请求.而一个页面请求可能包含有一到多个页面子帧,以及图片.CSS和插件等派生子资源.Page类就是用来对应这样的 ...
- WebKit 内核源码分析 (四)
本文介绍 WebCore 中 Loader 模块是如何加载资源的,分主资源和派生资源分析 loader 模块的类关系. 关键词: WebKit,Loader,Network,ResouceLoader ...
- WebKit 内核源码分析 (一) Frame
在分析内核的时候,Frame是首当其冲的一个类,本文将分析Frame类的代码. 1. 描述 Frame类是WebCore内核同应用之间联系的一个重要的类.它有点像设计模式中的Façade,将内核 ...
- WebKit 内核源码分析 (二) FrameLoader
FrameLoader类负责一个Frame的加载,在Frame的流程中起到非常重要的重要,同很多组件都有交互,本文将分析FrameLoader类的代码. 1. 概述 顾名思义,FrameLoader是 ...
- Linux内核源码分析方法
说明:这是一个刚接触内核三个月的同学,通过对一个内核特性的分析,总结出一套分析内核的方法. 一.内核源码之我见 Linux内核代码的庞大令不少人"望而生畏",也正因为如此,使得人们 ...
- LiteOS内核源码分析:位操作模块
摘要:本文带领大家一起剖析了LiteOS位操作模块的源代码,代码非常简单,参考官方示例程序代码,实际编译运行一下,加深理解. 本文分享自华为云社区<LiteOS内核源码分析系列五 LiteOS内 ...
- Linux内核源码分析方法—程序员进阶必备
一.内核源码之我见 Linux内核代码的庞大令不少人"望而生畏",也正因为如此,使得人们对Linux的了解仅处于泛泛的层次.如果想透析Linux,深入操作系统的本质,阅读内核源码是 ...
- 内核大佬讲述,Linux内核源码分析方法(建议收藏)
一.内核源码之我见 Linux内核代码的庞大令不少人"望而生畏",也正因为如此,使得人们对Linux的了解仅处于泛泛的层次.如果想透析Linux,深入操作系统的本质,阅读内核源码是 ...
- 鸿蒙内核源码分析:调度机制篇
作者 | 深入研究鸿蒙,鸿蒙内核发烧友 出品 | CSDN(ID:CSDNnews) 头图 | CSDN 下载自东方 IC 阅读之前建议先读本系列其他文章,以便对本文任务调度机制的理解. 为什么要学这 ...
最新文章
- Android -- Intent
- 深入理解javascript选择器API系列第二篇——getElementsByClassName
- IDC公布2017年亚太地区数据中心10大预测
- Vue2 学习笔记3
- 死磕 synchronized 底层实现
- 天天象棋 残局闯关 第1关
- oracle全局批准供应商,Oracle EBS-SQL (PO-7):检查异常-非批准的供应商设置供货比例.sql...
- spark:SparkUI界面
- python3提取字符串中的数字_如何在Python中从字符串中提取数字?
- DBATools PowerShell SQL Server数据库备份命令
- 数据结构 6-0 图
- PyQt5快速入门教程4-QtDesigner设计登录界面
- 终端安全防护技术研究(一)
- AI三驾马车之深度学习框架
- 高通CPU处理器解析
- 让一群脑洞清奇的开发者告诉你,AI+产业的N种可能
- 京东1.8联合会员腾讯视频QQ音乐转手教程 京东app换绑
- java8的LocalDate、LocalDateTime和之前的Date、Calendar
- CodeM资格赛E 数码 题解
- 【2023版】超详细Python+Pycharm安装保姆级教程,Python环境配置和使用指南,看完这一篇就够了