浏览器是怎样工作的二:渲染引擎 HTML解析(3)

时间:2012-05-09 20:34:20 

解析算法

如我们前面看到的,HTML无法使用自上而下或自下而上的解析器来解析。

理由如下:

  1. 语言的宽容特点

  2. 浏览器需要对无效HTML提供容错性的事实。

  3. 解析过程的反复。通常解析过程中源码不会变化。但在HTML中,script标签包含"document.write"时可以添加内容,即解析过程实际上还会改变源码。

浏览器创建了自己的解析器来解析HTML文档。

HTML5规范里对解析算法有具体的说明,解析由两部分组成:分词与构建树。

分词属于词法分析部分,它把输入解析成符号序列。在HTML中符号就是开始标签,结束标签,属性名称和属生值。

分词器识别这些符号并将其送入树构建者,然后继续分析处理下一个符号,直到输入结束。

图 6: HTML解析流程 (源自HTML5规范)

分词算法

算法的输出是HTML符号。算法可以用状态机来描述。 每一个状态从输入流中消费一个或多个字符,并根据它们更新下一状态。决策受当前符号状态和树的构建状态影响。这意味着同样的字符可能会产生不同的结果,取决于当前的状态。算法太复杂,我们用一个例子来看看它的原理。

基础示例,分析下面的标签:

  1. <html>  

  2.     <body>  

  3.         Hello world   

  4.     body>  

  5.  html>  

初始状态是"Data state",当遇到"<"时状态改为"Tag open state"。吃掉"a-z"字符组成的符号后产生了"Start tag token",状态变更为"Tag name state"。我们一直保持此状态,直到遇到">"。每个字符都被追加到新的符号名上。在我们的例子中,解出的符号就是"html"。

当碰到">"时,当前符号完成,状态改回"Data state"。""标签将会以同样的方式处理。现在"html"与"body"标签都完成了,我们回到"Data state"状态。吃掉"H"("Hello world"第一个字母)时会产生一个字符符号,直到碰到""的"<"符号,我们就完成了一个字符符号"Hello world"。

现在我们回到"Tag open state"状态。吃掉下一个输入"/"时会产生一个"end tag token"并变更为"Tag name state"状态。同样,此状态保持到我们碰到">"时。这时新标签符号完成,我们又回到"Data state"。同样""也会被这样处理。

图 9: 示例输入源的分词处理

树的构建算法

当解析器被创建时,文档对象也被创建了。在树的构建过程中DOM树的根节点(Documen)将被修改,元素被添加到上面去。每个分词器完成的节点都会被树构建器处理。规范中定义了每一个符号与哪个DOM对象相关。除了把元素添加到DOM树外,它还会被添加到一个开放元素堆栈。这个堆栈用于纠正嵌套错误和标签未关闭错误。这个算法也用状态机描述,它的状态叫做"insertion modes"。

让我们看看下面输入的树构建过程:

  1. <html>  

  2.     <body>  

  3.         Hello world   

  4.     body>  

  5.  html>  

树的构建过程中,输入就是分词过程中得到的符号序列。第一个模式叫"initial mode"。接收 html 符号后会变成"before html"模式并重新处理此模式中的符号。这会创建一个HTMLHtmlElement元素并追加到根文档节点。

然后状态改变为"before head"。我们收到"body"时,会隐式创建一个HTMLHeadElement,尽管我们没有这个标签,它也会被创建并添加到树中。

现在我们进入"in head"模式,然后是"after head",Body会被重新处理,创建HTMLBodyElement元素并插入,然后进入"in body"模式。

字符符号"Hello world"收到后会创建一个"Text"节点,所有字符都被一一追加到上面。

收到body结束标签后进入 "after body" 模式,收到html结束标签后进入"after after body"模式。所有符号处理完后将终止解析。

图 10: 示例HTML树的构建

标签:浏览器,html解析,渲染引擎
0
投稿

猜你喜欢

  • mysql去重查询的三种方法小结

    2024-01-21 14:52:22
  • Windows 下 MySQL 8.X 的安装教程

    2024-01-14 03:05:33
  • python实现在多维数组中挑选符合条件的全部元素

    2022-06-02 03:43:12
  • python矩阵转换为一维数组的实例

    2023-09-13 18:34:10
  • python 实现任务管理清单案例

    2023-09-01 04:59:17
  • Python Web框架Flask信号机制(signals)介绍

    2022-12-01 13:52:23
  • 如何用Python进行时间序列分解和预测

    2022-06-20 14:39:42
  • Golang Mutex 原理详细解析

    2024-05-22 10:28:58
  • golang中tar压缩和解压文件详情

    2024-05-10 13:58:46
  • Python中关于元组 集合 字符串 函数 异常处理的全面详解

    2021-04-14 12:07:37
  • 保护Access 2000数据库的安全

    2008-10-23 13:55:00
  • python抢购软件/插件/脚本附完整源码

    2021-04-25 22:03:17
  • MySQL DISTINCT 的基本实现原理详解

    2024-01-15 17:21:29
  • python循环定时中断执行某一段程序的实例

    2023-10-09 22:11:20
  • MySQL数据库生产环境的维护工作总结的经验

    2011-12-01 10:20:52
  • 在SQL Server中使用索引的技巧

    2009-02-24 17:50:00
  • MySQL中Decimal类型和Float Double的区别(详解)

    2024-01-26 16:37:55
  • 通过js获取div的background-image属性

    2023-08-23 06:07:23
  • python中mechanize库的简单使用示例

    2021-05-22 14:32:12
  • Python cv2 图像自适应灰度直方图均衡化处理方法

    2022-09-21 17:01:57
  • asp之家 网络编程 m.aspxhome.com