解析过程是迭代的,解析器从词法分析器处取道一个新的符号,并试着用这个符号匹配一条语法规则,如果匹配了一条规则,这个符号对应的节点将被添加到解析树上,然后解析器请求另一个符号。如果没有匹配到规则,解析器将在内部生存该符号,并从词法分析器取下一个符号,直到所有内部生存的符号能够匹配一项语法规则。如果最终没有找到匹配的规则,解析器将抛出一个非常,这意味着文档无效或是包含语法错误。
4.4 HTML 解析
解析算法 The parsing algorithm
hmtl 不能被一样平常的解析器(自顶向下或自底向上)所解析,缘故原由是:
Html5规范中描述了这个解析算法,算法包罗两个阶段——符号化及构建立。
符号化是词法分析的过程,将输入解析为符号,html的符号包罗开始标签、竣事标签、属性名及属性值。
符号辨认器辨认出符号后,将其传递给树构建器,并读取下一个字符,以辨认下一个符号,这样直随处置惩罚完所有输入。
HTML 解析流程:
符号辨认算法 The tokenization algorithm
算法输出html符号,该算法用状态机表示。每次读取输入流中的一个或多个字符,并根据这些字符转移到下一个状态,当前的符号状态及构建立状态共同影响效果,这意味着,读取同样的字符,可能由于当前状态的差异,得到差异的效果以进入下一个精确的状态。
这个算法很复杂,这里用一个简单的例子来解释这个原理。
根本示例——符号化下面的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”。后面的“”将和“”一样处置惩罚。
符号化示例输入:
树的构建算法 Tree construction algorithm
在树的构建阶段,将修改以Document为根的DOM树,将元素附加到树上。每个由符号辨认器辨认天生的节点将会被树构造器举行处置惩罚,规范中定义了每个符号相对应的Dom元素,对应的Dom元素将会被创建。这些元素除了会被添加到Dom树上,还将被添加到开放元素堆栈中。这个堆栈用来纠正嵌套的未匹配和未闭合标签,这个算法也是用状态机来描述,所有的状态采用插入模式。
示例中树的创建过程:
元素的范例也需要思量,例如,表单控件和表格带有特殊的框架。
在webkit中,如果一个元素想创建一个特殊的渲染对象,它需要复写“createRenderer”方法,使渲染对象指向不包含几何信息的样式对象。 渲染树和Dom树的关系 The render tree relation to the DOM tree
渲染对象和Dom元素相对应,但这种对应关系不是一对一的,不可见的Dom元素不会被插入渲染树,例如head元素。另外,display属性为none的元素也不会在渲染树中出现(visibility属性为hidden的元素将出如今渲染树中)。
另有一些Dom元素对应几个可见对象,它们一样平常是一些具有复杂结构的元素,无法用一个矩形来描述。例如,select元素有三个渲染对象——一个显示区域、一个下拉列表及一个按钮。同样,当文本由于宽度不够而折行时,新行将作为额外的渲染元素被添加。另一个多个渲染对象的例子是不规范的html,根据css规范,一个行内元素只能仅包含行内元素或仅包含块状元素,在存在肴杂内容时,将会创建匿名的块状渲染对象包裹住行内元素。
一些渲染对象和所对应的Dom节点不在树上雷同的位置,例如,浮动和绝对定位的元素在文本流之外,在两棵树上的位置差异,渲染树上标识出真实的结构,并用一个占位结构标识出它们原来的位置。
渲染树及对应的Dom树:
创建立的流程 The flow of constructing the tree
Firefox中,表述为一个监听Dom更新的监听器,将frame的创建委派给Frame Constructor,这个构建器计算样式(参看样式计算)并创建一个frame。
Webkit中,计算样式并天生渲染对象的过程称为attachment,每个Dom节点有一个attach方法,attachment的过程是同步的,调用新节点的attach方法将节点插入到Dom树中。
处置惩罚html和body标签将构建渲染树的根,这个根渲染对象对应被css规范称为containing block的元素——包含了其他所有块元素的顶级块元素。它的大小就是viewport——浏览器窗口的显示区域,Firefox称它为viewPortFrame,webkit称为RenderView,这个就是文档所指向的渲染对象,树中其他的部分都将作为一个插入的Dom节点被创建。 样式计算 Style Computation
创建渲染树需要计算出每个渲染对象的可视属性,这可以通过计算每个元素的样式属性得到。
样式包罗各种来源的样式表,行内样式元素及html中的可视化属性(例如bgcolor),可视化属性转化为css样式属性。
样式表来源于浏览器默认样式表,及页面作者和用户提供的样式表——有些样式是浏览器用户提供的(浏览器答应用户定义喜欢的样式,例如,在Firefox中,可以通过在Firefox Profile目录下放置样式表实现)。
计算样式的一些困难:
样式数据是非常大的结构,生存大量的样式属性会带来内存问题
如果不举行优化,找到每个元素匹配的规则会导致性能问题,为每个元素查找匹配的规则都需要遍历整个规则表,这个过程有很大的工作量。选择符可能有复杂的结构,匹配过程如果沿着一条开始看似精确,厥后却被证明是无用的路径,则必须去实验另一条路径。
例如,下面这个复杂选择符
div div div div{…}
这意味着规则应用到三个div的子女div元素,选择树上一条特定的路径去查抄,这可能需要遍历节点树,最后却发现它只是两个div的子女,并不利用该规则,然后则需要沿着另一条路径去实验