本文摘要说明现代浏览器是如何解析HTML的:
渲染引擎的职责就是渲染,即在浏览器窗口中显示所请求的内容。
默认情况下,渲染引擎可以显示html、xml文档及图片,它也可以借助插件(一种浏览器扩展)显示其他类型数据,例如使用PDF阅读器插件,可以显示PDF格式,将由专门一章讲解插件及扩展,这里只讨论渲染引擎最主要的用途——显示应用了CSS之后的html及图片。
渲染引擎首先通过网络获得所请求文档的内容,通常以8K分块的方式完成。
下面是渲染引擎在取得内容之后的基本流程:
解析html以构建dom树->构建render树->布局render树->绘制render树
图2:渲染引擎基本流程
渲染引擎开始解析html,并将标签转化为内容树中的dom节点。接着,它解析外部CSS文件及style标签中的样式信息。这些样式信息以及html中的可见性指令将被用来构建另一棵树——render树。
Render树由一些包含有颜色和大小等属性的矩形组成,它们将被按照正确的顺序显示到屏幕上。
Render树构建好了之后,将会执行布局过程,它将确定每个节点在屏幕上的确切坐标。再下一步就是绘制,即遍历render树,并使用UI后端层绘制每个节点。
值得注意的是,这个过程是逐步完成的,为了更好的用户体验,渲染引擎将会尽可能早的将内容呈现到屏幕上,并不会等到所有的html都解析完成之后再去构建和布局render树。它是解析完一部分内容就显示一部分内容,同时,可能还在通过网络下载其余内容。
图3:webkit主流程
图4:Mozilla的Geoko 渲染引擎主流程
从图3和4中可以看出,尽管webkit和Gecko使用的术语稍有不同,他们的主要流程基本相同。Gecko称可见的格式化元素组成的树为frame树,每个元素都是一个frame,webkit则使用render树这个名词来命名由渲染对象组成的树。Webkit中元素的定位称为布局,而Gecko中称为回流。Webkit称利用dom节点及样式信息去构建render树的过程为attachment,Gecko在html和dom树之间附加了一层,这层称为内容接收器,相当制造dom元素的工厂。下面将讨论流程中的各个阶段。
跟HTML的变体XHTML不同,HTML更宽容,它允许忽略一些特定标签,有时可以省略开始或结束标签,这个特点让HTML的解析更加困难。
1.使用解析算法 The parsing algorithm进行符号化和构建树
符号化是词法分析的过程,将输入解析为符号,html的符号包括开始标签、结束标签、属性名及属性值。
符号识别器识别出符号后,将其传递给树构建器,并读取下一个字符,以识别下一个符号,这样直到处理完所有输入。
2.符号识别算法 The tokenization algorithm
基本示例——符号化下面的html:
<html>
<body>
Hello world
body>
html>
初始状态为“Data State”,当遇到“<”字符,状态变为“Tag open state”,读取一个a-z的字符将产生一个开始标签符号,状态相应变为“Tag name state”,一直保持这个状态直到读取到“>”,每个字符都附加到这个符号名上,例子中创建的是一个html符号。
当读取到“>”,当前的符号就完成了,此时,状态回到“Data state”,“”重复这一处理过程。到这里,html和body标签都识别出来了。现在,回到“Data state”,读取“Hello world”中的字符“H”将创建并识别出一个字符符号,这里会为“Hello world”中的每个字符生成一个字符符号。
这样直到遇到“”中的“<”。现在,又回到了“Tag open state”,读取下一个字符“/”将创建一个闭合标签符号,并且状态转移到“Tag name state”,还是保持这一状态,直到遇到“>”。然后,产生一个新的标签符号并回到“Data state”。后面的“”将和“”一样处理。
图10:符号化示例输入
3.树的构建算法 Tree construction algorithm
来看一下示例中树的创建过程:
<html>
<body>
Hello world
body>
html>
首先是“initial mode”,接收到html符号后将转换为“before html”模式,在这个模式中对这个符号进行再处理。此时,创建了一个HTMLHtmlElement元素,并将其附加到根Document对象上。
状态此时变为“before head”,接收到body符号时,即使这里没有head符号,也将自动创建一个HTMLHeadElement元素并附加到树上。
现在,转到“in head”模式,然后是“after head”。到这里,body符号会被再次处理,将创建一个HTMLBodyElement并插入到树中,同时,转移到“in body”模式。
然后,接收到字符串“Hello world”的字符符号,第一个字符将导致创建并插入一个text节点,其他字符将附加到该节点。
接收到body结束符号时,转移到“after body”模式,接着接收到html结束符号,这个符号意味着转移到了“after after body”模式,当接收到文件结束符时,整个解析过程结束。
步骤就是先生成HTML解析符号,再将符号构建成Element树
未完待续…
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
head>
<body>
<div style="font-family: msyh;text-align: center;">
<h2>早发白帝城h2>
<div style="padding: 20px 20px; background-color: rgb(250, 192, 143);font-family: simkai;">
朝辞白帝彩云间,千里江陵一日还。<br />
两岸猿声啼不住,轻舟已过万重山。<br />
div>
<h2>赠汪伦h2>
<div style="padding: 20px 20px; background-color: rgb(250, 192, 143);font-family: simkai;">
李白乘舟将欲行,忽闻岸上踏歌声。<br />
桃花潭水深千尺,不及汪伦送我情。<br />
div>
<h2>望庐山瀑布h2>
<div style="padding: 20px 20px; background-color: rgb(250, 192, 143);font-family: simkai;">
日照香炉生紫烟,遥看瀑布挂前川。<br />
飞流直下三千尺,疑是银河落九天。<br />
div>
div>
<div style="text-shadow: 5px 5px 5px #787878;color: #fff;background-color: #CDC9C9;">
<p><span style="margin-right: 10px;">☆span>发:启程。白帝城:故址在今重庆市奉节县白帝山上。p>
<p><span style="margin-right: 10px;">☆span>踏歌:唐代民间流行的一种手拉手、两足踏地为节拍的歌舞形式,可以边走边唱。p>
<p><span style="margin-right: 10px;">☆span>桃花潭:在今安徽泾县西南一百里。《一统志》谓其深不可测。深千尺:诗人用潭水深千尺比喻汪伦与他的友情,运用了夸张的手法。p>
div>
body>
html>