媒介
史上最长的分享,编辑的时间 表现 感情 稳固 。本日 早读文章必要 你很长的时间阅读 ,由@ikeike443 和 @kiyoto01翻译分享。
正文从这开始~
这是一篇全面先容 WebKit 和 Gecko 内部操纵 的入门文章,是以色列开辟 职员 塔利·加希尔大量研究的结果 。在已往 的几年中,她查阅了全部 公开辟 布的关于欣赏 器内部机制的数据(请拜见 资源) ,并花了很多 时间来研读网络欣赏 器的源代码 。她写道:
在 IE 占据 90% 市场份额的年代,我们除了把欣赏 器当成一个“黑箱”,什么也做不了。但是如今 ,开放源代码的欣赏 器拥有了过半的市场份额,因此,是时间 来揭开秘密 的面纱,一探网络欣赏 器的黑幕 了。呃 ,内里 只有数以百万行计的 C++ 代码 ...
塔利在她的网站上公布了本身 的研究结果 ,但是我们以为 它值得让更多的人来相识 ,以是 我们在此重新整理并公布 。
作为一名网络开辟 职员 ,学习欣赏 器的内部工作原理将有助于您作出更明智的决定 ,并明白 那些最佳开辟 实践的个中缘由。只管 这是一篇相称 长的文档,但是我们发起 您花些时间来细致 阅读;读完之后 ,您肯定会以为 所费不虚。
保罗·爱丽诗 (Paul Irish),Chrome 欣赏 器开辟 职员 事件 部
简介
网络欣赏 器很大概 是利用 最广的软件。在这篇入门文章中,我将会先容 它们的幕后工作原理 。我们会相识 到 ,从您在地点 栏输入 google.com 直到您在欣赏 器屏幕上看到 Google 首页的整个过程中都发生了些什么。
我们要讨论的欣赏 器
如今 利用 的主流欣赏 器有五个:Internet Explorer、Firefox 、Safari、Chrome 欣赏 器和 Opera。本文中以开放源代码欣赏 器为例,即 Firefox、Chrome 欣赏 器和 Safari(部分 开源) 。根据 StatCounter 欣赏 器统计数据,如今 (2011 年 8 月)Firefox 、Safari 和 Chrome 欣赏 器的总市场占据 率将近 60%。由此可见 ,如今 开放源代码欣赏 器在欣赏 器市场中占据了非常坚固 的部分 。
欣赏 器的重要 功能
欣赏 器的重要 功能就是向服务器发出哀求 ,在欣赏 器窗口中展示您选择的网络资源 。这里所说的资源一样平常 是指 HTML 文档,也可以是 PDF、图片或其他的范例 。资源的位置由用户利用 URI(同一 资源标示符)指定。
欣赏 器表明 并表现 HTML 文件的方式是在 HTML 和 CSS 规范中指定的 。这些规范由网络标准 化构造 W3C(万维网同盟 )举行 维护。
多年以来,各欣赏 器都没有完全服从 这些规范 ,同时还在开辟 本身 独有的扩展程序,这给网络开辟 职员 带来了严峻 的兼容性题目 。如今 ,大多数的欣赏 器都是或多或少地服从 规范 。
欣赏 器的用户界面有很多 相互 雷同 的元素 ,此中 包罗 :
用来输入 URI 的地点 栏
进步 和退却 按钮
书签设置选项
用于革新 和克制 加载当前文档的革新 和克制 按钮
用于返回主页的主页按钮
奇怪 的是,欣赏 器的用户界面并没有任何正式的规范,这是多年来的最佳实践天然 发展以及相互 之间相互模仿 的结果 。HTML5 也没有界说 欣赏 器必须具有的用户界面元素 ,但列出了一些通用的元素,比方 地点 栏、状态栏和工具栏等。固然 ,各欣赏 器也可以有本身 独特的功能 ,比如 Firefox 的下载管理器。
欣赏 器的高层布局
欣赏 器的重要 组件为 (1.1):
用户界面 - 包罗 地点 栏 、进步 /退却 按钮、书签菜单等 。除了欣赏 器主窗口表现 的您哀求 的页面外,其他表现 的各个部分 都属于用户界面。
欣赏 器引擎 - 在用户界面和出现 引擎之间传送指令。
出现 引擎 - 负责表现 哀求 的内容 。假如 哀求 的内容是 HTML,它就负责分析 HTML 和 CSS 内容 ,并将分析 后的内容表现 在屏幕上。
网络 - 用于网络调用,比如 HTTP 哀求 。其接口与平台无关,并为全部 平台提供底层实现 。
用户界面后端 - 用于绘制根本 的窗口小部件,比如 组合框和窗口。其公开了与平台无关的通用接口 ,而在底层利用 操纵 体系 的用户界面方法。
Java 表明 器 。用于分析 和实行 Java 代码。
数据存储。这是长期 层 。欣赏 器必要 在硬盘上生存 各种数据,比方 Cookie。新的 HTML 规范 (HTML5) 界说 了“网络数据库 ”,这是一个完备 (但是轻便 )的欣赏 器内数据库。
欣赏 器的重要 组件
值得留意 的是 ,和大多数欣赏 器差别 ,Chrome 欣赏 器的每个标签页都分别对应一个出现 引擎实例。每个标签页都是一个独立的进程 。
出现 引擎
出现 引擎的作用嘛...固然 就是“出现 ”了,也就是在欣赏 器的屏幕上表现 哀求 的内容。
默认环境 下 ,出现 引擎可表现 HTML 和 XML 文档与图片。通过插件(或欣赏 器扩展程序),还可以表现 其他范例 的内容;比方 ,利用 PDF 查察 器插件就能表现 PDF 文档 。但是在本章中 ,我们将会合 先容 其重要 用途:表现 利用 CSS 格式化的 HTML 内容和图片。
出现 引擎
本文所讨论的欣赏 器(Firefox、Chrome 欣赏 器和 Safari)是基于两种出现 引擎构建的。Firefox 利用 的是 Gecko,这是 Mozilla 公司“自制”的出现 引擎 。而 Safari 和 Chrome 欣赏 器利用 的都是 WebKit。
WebKit 是一种开放源代码出现 引擎,早先 用于 Linux 平台 ,随后由 Apple 公司举行 修改,从而支持苹果机和 Windows。有关详情,请参阅 webkit.org 。
主流程
出现 引擎一开始会从网络层获取哀求 文档的内容,内容的巨细 一样平常 限定 在 8000 个块以内。
然后举行 如下所示的根本 流程:
出现 引擎的根本 流程
出现 引擎将开始分析 HTML 文档 ,并将各标记 逐个转化成“内容树 ”上的 DOM 节点。同时也会分析 外部 CSS 文件以及样式元素中的样式数据 。HTML 中这些带有视觉指令的样式信息将用于创建另一个树布局 :出现 树。
出现 树包罗 多个带有视觉属性(如颜色和尺寸)的矩形。这些矩形的分列 次序 就是它们将在屏幕上表现 的次序 。
出现 树构建完毕之后,进入“布局 ”处理 惩罚 阶段,也就是为每个节点分配一个应出如今 屏幕上简直 切坐标 。下一个阶段是绘制 - 出现 引擎会遍历出现 树 ,由用户界面后端层将每个节点绘制出来。
必要 偏重 指出的是,这是一个渐进的过程。为到达 更好的用户体验,出现 引擎会力图 尽快将内容表现 在屏幕上 。它不必比及 整个 HTML 文档分析 完毕之后 ,就会开始构建出现 树和设置布局 。在不绝 吸取 和处理 惩罚 来自网络的别的 内容的同时,出现 引擎会将部分 内容分析 并表现 出来。
主流程示例
WebKit 主流程
Mozilla 的 Gecko 出现 引擎主流程
从图 3 和图 4 可以看出,固然 WebKit 和 Gecko 利用 的术语略有差别 ,但团体 流程是根本 雷同 的 。
Gecko 将视觉格式化元素构成 的树称为“框架树”。每个元素都是一个框架。WebKit 利用 的术语是“出现 树 ”,它由“出现 对象”构成 。对于元素的放置,WebKit 利用 的术语是“布局 ” ,而 Gecko 称之为“重排”。对于毗连 DOM 节点和可视化信息从而创建出现 树的过程,WebKit 利用 的术语是“附加 ”。有一个渺小 的非语义差别 ,就是 Gecko 在 HTML 与 DOM 树之间尚有 一个称为“内容槽”的层,用于天生 DOM 元素 。我们会逐一叙述 流程中的每一部分 :
分析 - 综述
分析 是出现 引擎中非常紧张 的一个环节 ,因此我们要更深入地讲授 。起首 ,来先容 一下分析 。
分析 文档是指将文档转化成为故意 义的布局 ,也就是可让代码明白 和利用 的布局 。分析 得到的结果 通常是代表了文档布局 的节点树 ,它称作分析 树大概 语法树 。
示例 - 分析 2 + 3 - 1 这个表达式,会返回下面的树:
数学表达式树节点
语法
分析 是以文档所依照 的语法规则(编写文档所用的语言或格式)为底子 的。全部 可以分析 的格式都必须对应确定的语法(由词汇和语法规则构成)。这称为与上下文无关的语法 。人类语言并不属于如许 的语言,因此无法用通例 的分析 技能 举行 分析 。
分析 器和词法分析器的组合
分析 的过程可以分成两个子过程:词法分析和语法分析。
词法分析是将输入内容分割成大量标记 的过程 。标记 是语言中的词汇 ,即构成内容的单位 。在人类语言中,它相称 于语言字典中的单词。
语法分析是应用语言的语法规则的过程 。
分析 器通常将分析 工作分给以下两个组件来处理 惩罚 :词法分析器(偶然 也称为标记 天生 器),负责将输入内容分解成一个个有效 标记 ;而分析 器负责根据语言的语法规则分析文档的布局 ,从而构建分析 树。词法分析器知道怎样 将无关的字符(比如 空格和换行符)分离出来。
从源文档到分析 树
分析 是一个迭代的过程 。通常,分析 器会向词法分析器哀求 一个新标记 ,并实行 将其与某条语法规则举行 匹配。假如 发现了匹配规则 ,分析 器会将一个对应于该标记 的节点添加到分析 树中,然后继承 哀求 下一个标记 。
假如 没有规则可以匹配,分析 器就会将标记 存储到内部,并继承 哀求 标记 ,直至找到可与全部 内部存储的标记 匹配的规则。假如 找不到任何匹配规则,分析 器就会引发一个非常 。这意味着文档无效,包罗 语法错误。
翻译
很多 时间 ,分析 树还不是终极 产物 。分析 通常是在翻译过程中利用 的,而翻译是指将输入文档转换成另一种格式 。编译就是如许 一个例子。编译器可将源代码编译成呆板 代码,具体 过程是起首 将源代码分析 成分析 树 ,然后将分析 树翻译成呆板 代码文档。
编译流程
分析 示例
在图 5 中,我们通过一个数学表达式创建 了分析 树 。如今 ,让我们试着界说 一个简单 的数学语言 ,用来演示分析 的过程。
词汇:我们用的语言可包罗 整数、加号和减号。
语法:
构成语言的语法单位 是表达式 、项和运算符 。
我们用的语言可以包罗 恣意 数量 的表达式。
表达式的界说 是:一个“项”接一个“运算符 ”,然后再接一个“项”。
运算符是加号或减号 。
项是一个整数或一个表达式。
让我们分析一下 2 + 3 - 1。
匹配语法规则的第一个子串是 2,而根据第 5 条语法规则 ,这是一个项。匹配语法规则的第二个子串是 2 + 3,而根据第 3 条规则(一个项接一个运算符,然后再接一个项),这是一个表达式 。下一个匹配项已经到了输入的竣事 。2 + 3 - 1 是一个表达式 ,由于 我们已经知道 2 + 3 是一个项,如许 就符合“一个项接一个运算符,然后再接一个项”的规则。2 + + 不与任何规则匹配 ,因此是无效的输入 。
词汇和语法的正式界说
词汇通常用正则表达式表现 。
比方 ,我们的示例语言可以界说 如下:
INTEGER :0|[1-9][0-9]*
PLUS :+
MINUS:-
正如您所看到的,这里用正则表达式给出了整数的界说 。
语法通常利用 一种称为 BNF 的格式来界说 。我们的示例语言可以界说 如下:
expression :=term operation term
operation :=PLUS |MINUS
term :=INTEGER |expression
之前我们说过 ,假如 语言的语法是与上下文无关的语法,就可以由通例 分析 器举行 分析 。与上下文无关的语法的直观界说 就是可以完全用 BNF 格式表达的语法。有关正式界说 ,请参阅关于与上下文无关的语法的维基百科文章 。
分析 器范例
有两种根本 范例 的分析 器:自上而下分析 器和自下而上分析 器。直观地来说 ,自上而下的分析 器从语法的高层布局 出发,实行 从中找到匹配的布局 。而自下而上的分析 器从低层规则出发,将输入内容渐渐 转化为语法规则 ,直至满意 高层规则 。
让我们来看看这两种分析 器会怎样 分析 我们的示例:
自上而下的分析 器会从高层的规则开始:起首 将 2 + 3 标识为一个表达式,然后将 2 + 3 - 1 标识为一个表达式(标识表达式的过程涉及到匹配其他规则,但是出发点 是最高级别的规则)。
自下而上的分析 器将扫描输入内容,找到匹配的规则后 ,将匹配的输入内容更换 成规则。云云 继承 更换 ,直到输入内容的末了 。部分 匹配的表达式生存 在分析 器的堆栈中 。
这种自下而上的分析 器称为移位归约分析 器,由于 输入在向右移位(假想 有一个指针从输入内容的开头移动到末了 ) ,而且 渐渐 归约到语法规则上。
主动 天生 分析 器
有一些工具可以资助 您天生 分析 器,它们称为分析 器天生 器。您只要向其提供您所用语言的语法(词汇和语法规则),它就会天生 相应的分析 器 。创建分析 器必要 对分析 有深刻明白 ,而人工创建并优化分析 器并不是一件轻易 的事变 ,以是 分析 器天生 器黑白 常实用的。
WebKit 利用 了两种非常闻名 的分析 器天生 器:用于创建词法分析器的 Flex 以及用于创建分析 器的 Bison(您也大概 碰到 Lex 和 Yacc 如许 的别名)。Flex 的输入是包罗 标记 的正则表达式界说 的文件 。Bison 的输入是采取 BNF 格式的语言语法规则。
HTML 分析 器
HTML 分析 器的任务 是将 HTML 标记 分析 成分析 树。
HTML 语法界说
HTML 的词汇和语法在 W3C 构造 创建的规范中举行 了界说 。当前的版本是 HTML4,HTML5 正在处理 惩罚 过程中。
非与上下文无关的语法
正如我们在分析 过程的简介中已经相识 到的 ,语法可以用 BNF 等格式举行 正式界说 。
很遗憾,全部 的通例 分析 器都不实用 于 HTML(我并不是开顽笑 ,它们可以用于分析 CSS 和 Java) 。HTML 并不能很轻易 地用分析 器所需的与上下文无关的语法来界说 。
有一种可以界说 HTML 的正规格式:DTD(Document Type Definition ,文档范例 界说 ),但它不是与上下文无关的语法。
这初看起来很奇怪 :HTML 和 XML 非常相似。有很多 XML 分析 器可以利用 。HTML 存在一个 XML 变体 (XHTML),那么有什么大的区别呢?
区别在于 HTML 的处理 惩罚 更为“宽容 ”,它答应 您省略某些隐式添加的标记 ,偶然 还能省略一些起始大概 竣事 标记 等等。和 XML 严格 的语法差别 ,HTML 团体 来看是一种“软性”的语法。
显然,这种看上去渺小 的差别 实际 上却带来了巨大的影响 。一方面 ,这是 HTML 云云 盛行 的缘故起因 :它能包涵 您的错误,简化网络开辟 。另一方面,这使得它很难编写正式的语法。概括地说 ,HTML 无法很轻易 地通过通例 分析 器分析 (由于 它的语法不是与上下文无关的语法),也无法通过 XML 分析 器来分析 。
HTML DTD
HTML 的界说 采取 了 DTD 格式。此格式可用于界说 SGML 族的语言。它包罗 全部 答应 利用 的元素及其属性和条理 布局 的界说 。如上文所述,HTML DTD 无法构成与上下文无关的语法。
DTD 存在一些变体。严格 模式完全服从 HTML 规范 ,而其他模式可支持从前 的欣赏 器所利用 的标记 。如许 做的目标 是确保向下兼容一些早期版本的内容。最新的严格 模式 DTD 可以在这里找到:www.w3.org/TR/html4/strict.dtd
DOM
分析 器的输出“分析 树”是由 DOM 元素和属性节点构成的树布局 。DOM 是文档对象模子 (Document Object Model) 的缩写。它是 HTML 文档的对象表现 ,同时也是外部内容(比方 Java)与 HTML 元素之间的接口 。
分析 树的根节点是“Document ”对象。
DOM 与标记 之间险些 是逐一 对应的关系。比如 下面这段标记 :
html
body
p
Hello World
/p
divimg src="example.png"//div
/body
/html
可翻译成如下的 DOM 树:
示例标记 的 DOM 树
和 HTML 一样,DOM 也是由 W3C 构造 指定的 。请拜见 www.w3.org/DOM/DOMTR。这是关于文档操纵 的通用规范。此中 一个特定模块形貌 针对 HTML 的元素 。HTML 的界说 可以在这里找到:www.w3.org/TR/2003/REC-DOM-Level-2-HTML-20030109/idl-definitions.html。
我所说的树包罗 DOM 节点 ,指的是树是由实现了某个 DOM 接口的元素构成的。欣赏 器在具体 的实现中会有一些供内部利用 的其他属性 。
分析 算法
我们在之前章节已经说过,HTML 无法用通例 的自上而下或自下而上的分析 器举行 分析 。
缘故起因 在于:
语言的宽容本质。
欣赏 器向来 对一些常见的无效 HTML 用法采取 包涵 态度 。
分析 过程必要 不绝 地反复。源内容在分析 过程中通常不会改变,但是在 HTML 中,脚本标记 假如 包罗 document.write ,就会添加额外的标记 ,如许 分析 过程实际 上就更改了输入内容。
由于不能利用 通例 的分析 技能 ,欣赏 器就创建了自界说 的分析 器来分析 HTML。
HTML5 规范具体 地形貌 了分析 算法 。此算法由两个阶段构成 :标记 化和树构建。
标记 化是词法分析过程 ,将输入内容分析 成多个标记 。HTML 标记 包罗 起始标记 、竣事 标记 、属性名称和属性值 。
标记 天生 器辨认 标记 ,转达 给树构造器,然后担当 下一个字符以辨认 下一个标记 ;云云 反复直到输入的竣事 。
HTML 分析 流程(摘自 HTML5 规范)
标记 化算法
该算法的输出结果 是 HTML 标记 。该算法利用 状态机来表现 。每一个状态吸取 来自输入信息流的一个或多个字符 ,并根据这些字符更新下一个状态。当前的标记 化状态和树布局 状态会影响进入下一状态的决定。这意味着,纵然 吸取 的字符雷同 ,对于下一个精确 的状态也会产生差别 的结果 ,具体 取决于当前的状态 。该算法相称 复杂,无法在此详述,以是 我们通过一个简单 的示例来资助 各人 明白 其原理。
根本 示例 - 将下面的 HTML 代码标记 化:
html
body
Hello world
/body
/html
初始状态是数据状态。碰到 字符 时 ,状态更改为“标记 打开状态” 。吸取 一个 a-z 字符会创建“起始标记 ”,状态更改为“标记 名称状态 ”。这个状态会不停 保持到吸取 字符。在此期间吸取 的每个字符都会附加到新的标记 名称上。在本例中,我们创建的标记 是 html 标记 。
碰到 标记 时,会发送当前的标记 ,状态改回“数据状态”。body 标记 也会举行 同样的处理 惩罚 。如今 html 和 body 标记 均已发出 。如今 我们回到“数据状态”。吸取 到 Hello world 中的 H 字符时,将创建并发送字符标记 ,直到吸取 /body 中的 。我们将为 Hello world 中的每个字符都发送一个字符标记 。
如今 我们回到“标记 打开状态”。吸取 下一个输入字符 / 时 ,会创建 end tag token 并改为“标记 名称状态 ”。我们会再次保持这个状态,直到吸取 。然后将发送新的标记 ,并回到“数据状态”。/html 输入也会举行 同样的处理 惩罚 。
对示例输入举行 标记 化
树构建算法
在创建分析 器的同时 ,也会创建 Document 对象 。在树构建阶段,以 Document 为根节点的 DOM 树也会不绝 举行 修改,向此中 添加各种元素。标记 天生 器发送的每个节点都会由树构建器举行 处理 惩罚 。规范中界说 了每个标记 所对应的 DOM 元素 ,这些元素会在吸取 到相应的标记 时创建。这些元素不但 会添加到 DOM 树中,还会添加到开放元素的堆栈中 。此堆栈用于改正 嵌套错误和处理 惩罚 未关闭的标记 。其算法也可以用状态机来形貌 。这些状态称为“插入模式” 。
让我们来看看示例输入的树构建过程:
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 的树构建
分析 竣事 后的操纵
在此阶段 ,欣赏 器会将文档标注为交互状态,并开始分析 那些处于“deferred”模式的脚本,也就是那些应在文档分析 完成后才实行 的脚本。然后 ,文档状态将设置为“完成 ”,一个“加载”变乱 将随之触发 。
您可以在 HTML5 规范中查察 标记 化和树构建的完备 算法
欣赏 器的容错机制
您在欣赏 HTML 网页时从来不会看到“语法无效”的错误。这是由于 欣赏 器会改正 任何无效内容,然后继承 工作。
以下面的 HTML 代码为例:
html
mytag
/mytag
div
p
/div
Really lousy HTML
/p
/html
在这里 ,我已经违背 了很多 语法规则(“mytag ”不是标准 的标记 ,“p”和“div”元素之间的嵌套有误等等),但是欣赏 器仍旧 会精确 地表现 这些内容,而且 毫无怨言 。由于 有大量的分析 器代码会改正 HTML 网页作者的错误。
差别 欣赏 器的错误处理 惩罚 机制相称 同等 ,但令人称奇的是,这种机制并不是 HTML 当前规范的一部分 。和书签管理以及进步 /退却 按钮一样,它也是欣赏 器在多年发展中的产物 。很多 网站都广泛 存在着一些已知的无效 HTML 布局 ,每一种欣赏 器都会实行 通过和其他欣赏 器一样的方式来修复这些无效布局 。
HTML5 规范界说 了一部分 如许 的要求。WebKit 在 HTML 分析 器类的开头解释 中对此做了很好的概括 。
分析 器对标记 化输入内容举行 分析 ,以构建文档树。假如 文档的格式精确 ,就直接举行 分析 。
遗憾的是 ,我们不得不处理 惩罚 很多 格式错误的 HTML 文档,以是 分析 器必须具备肯定 的容错性。
我们至少要可以或许 处理 惩罚 以下错误环境 :
显着 不能在某些外部标记 中添加的元素 。在此环境 下,我们应该关闭全部 标记 ,直到出现克制 添加的元素,然后再参加 该元素。
我们不能直接添加的元素。这很大概 是网页作者忘记添加了此中 的一些标记 (大概 此中 的标记 是可选的) 。这些标签大概 包罗 :HTML HEAD BODY TBODY TR TD LI(尚有 遗漏的吗?)。
向 inline 元素内添加 block 元素。关闭全部 inline 元素,直到出现下一个较高级的 block 元素 。
假如 如许 仍旧 无效 ,可关闭全部 元素,直到可以添加元素为止,大概 忽略该标记 。
让我们看一些 WebKit 容错的示例:
利用 了 /br 而不是 br
有些网站利用 了 /br 而不是 br。为了与 IE 和 Firefox 兼容,WebKit 将其与 br 做同样的处理 惩罚 。
代码如下:
if(t-isCloseTag(brTag)m_document-inCompatMode()){
reportError(MalformedBRError);
t-beginTag =true;
}
请留意 ,错误处理 惩罚 是在内部举行 的,用户并不会看到这个过程。
离散表格
离散表格是指位于其他表格内容中,但又不在任何一个单位 格内的表格。
比如 以下的示例:
table
table
trtdinner table/td/tr
/table
trtdouter table/td/tr
/table
WebKit 会将其条理 布局 更改为两个同级表格:
table
trtdouter table/td/tr
/table
table
trtdinner table/td/tr
/table
代码如下:
if(m_inStrayTableContent localName ==tableTag)
popBlock(tableTag);
WebKit 利用 一个堆栈来生存 当前的元素内容 ,它会从外部表格的堆栈中弹出内部表格 。如今 ,这两个表格就变成 了同级关系。
嵌套的表单位 素
假如 用户在一个表单位 素中又放入了另一个表单,那么第二个表单将被忽略。
代码如下:
if(!m_currentFormElement){
m_currentFormElement =newHTMLFormElement(formTag,m_document);
}
过于复杂的标记 条理 布局
代码的解释 已经说得很清楚 了。
示例网站 www.liceo.edu.mx 嵌套了约 1500 个标记 ,全都来自一堆 b 标记 。我们只答应 最多 20 层同范例 标记 的嵌套,假如 再嵌套更多,就会全部忽略。
bool HTMLParser::allowNestedRedundantTag(constAtomicStringtagName)
{
unsigned i =0;
for(HTMLStackElem*curr =m_blockStack;
i cMaxRedundantTagDepth curr curr-tagName ==tagName;
curr =curr-next,i++){}
returni !=cMaxRedundantTagDepth;
}
放错位置的 html 大概 body 竣事 标记
同样 ,代码的解释 已经说得很清楚 了。
支持格式非常糟糕的 HTML 代码 。我们从不关闭 body 标记 ,由于 一些愚笨 的网页会在实际 文档竣事 之前就关闭。我们通过调用 end() 来实行 关闭操纵 。
if(t-tagName ==htmlTag ||t-tagName ==bodyTag )
return;
以是 网页作者必要 留意 ,除非您想作为反面讲义 出如今 WebKit 容错代码段的示例中 ,否则还请编写格式精确 的 HTML 代码 。
CSS 分析
还记得简介中分析 的概念吗?和 HTML 差别 ,CSS 是上下文无关的语法,可以利用 简介中形貌 的各种分析 器举行 分析 。究竟 上,CSS 规范界说 了 CSS 的词法和语法。
让我们来看一些示例:
词法语法(词汇)是针对各个标记 用正则表达式界说 的:
comment /*[^*]**+([^/*][^*]**+)*/
num [0-9]+|[0-9]*"."[0-9]+
nonascii [200-377]
nmstart [_a-z]|{nonascii}|{escape}
nmchar [_a-z0-9-]|{nonascii}|{escape}
name {nmchar}+
ident {nmstart}{nmchar}*
“ident ”是标识符 (identifier) 的缩写 ,比如 类名 。“name”是元素的 ID(通过“#”来引用)。
语法是采取 BNF 格式形貌 的。
ruleset
:selector [','S*selector ]*
'{'S*declaration [';'S*declaration ]*'}'S*
;
selector
:simple_selector [combinator selector |S+[combinator?selector ]?]?
;
simple_selector
:element_name [HASH |class|attrib |pseudo ]*
|[HASH |class|attrib |pseudo ]+
;
class
:'.'IDENT
;
element_name
:IDENT |'*'
;
attrib
:'['S*IDENT S*[['='|INCLUDES |DASHMATCH ]S*
[IDENT |STRING ]S*]']'
;
pseudo
:':'[IDENT |FUNCTION S*[IDENT S*]')']
;
表明 :这是一个规则集的布局 :
div.error ,a.error {
color:red;
font-weight:bold;
}
div.error 和 a.error 是选择器 。大括号内的部分 包罗 了由此规则集应用的规则。此布局 的正式界说 是如许 的:
ruleset
:selector [','S*selector ]*
'{'S*declaration [';'S*declaration ]*'}'S*
;
这表现 一个规则集就是一个选择器,大概 由逗号和空格(S 表现 空格)分隔的多个(数量 可选)选择器。规则集包罗 了大括号,以及此中 的一个或多个(数量 可选)由分号分隔的声明。“声明 ”和“选择器”将由下面的 BNF 格式界说 。
关于本文
译者:@ikeike443 和 @kiyoto01
原文:https://www.html5rocks.com/zh/tutorials/internals/howbrowserswork/