做为一名网络开发人员,学习浏览器的内部工做原理将有助于您做出更明智的决策,并理解那些最佳开发实践的个中原因。尽管这是一篇至关长的文档,可是咱们建议您花些时间来仔细阅读;读完以后,您确定会以为所费不虚。 css
保罗·爱丽诗(Paul Irish),Chrome 浏览器开发人员事务部html
浏览器是使用最广的软件之一。这篇博文中,我将简单介绍浏览器的工做原理。经过阅读本文,咱们将会了解,从您在地址栏输入 google.com ,直到您在浏览器屏幕上看到 Google 首页的整个过程当中都发生了些什么。web
浏览器的主要功能就是向服务器发出请求,在浏览器窗口中展现您想要访问的网络资源。资源的位置由用户使用 URI(统一资源标示符)指定。正则表达式
浏览器的主要组件包括:算法
图1.1:浏览器组件编程
渲染引擎是浏览器的核心,也能够叫作浏览器的内核。他的功能是渲染,即在浏览器窗口中显示所请求的内容。默认状况下,渲染引擎可显示 HTML 和 XML 文档及图片。后端
Firefox 使用的是 Gecko,这是 Mozilla 公司“自制”的渲染引擎。而 Safari 和 Chrome(28版本之前)浏览器使用的都是 Webkit。2013年7月10日发布的Chrome 28 版本中,Chrome浏览器开始正式使用Blink内核。因此,Webkit已经成为了Chrome浏览器的前内核。浏览器
渲染引擎一开始会从网络层获取请求文档的内容(以8k分块)。缓存
解析HTML文档,并将文档中的标签转化为dom节点树,即”内容树”。同时,它也会解析外部CSS文件以及style标签中的样式数据。这些样式信息连同HTML中的”可见内容”一道,被用于构建另外一棵树——”渲染树(Render树)”。服务器
渲染树由一些带有视觉属性(如颜色、大小等)的矩形组成,这些矩形将按照正确的顺序显示在频幕上。
渲染树构建完毕以后,将会进入”布局”处理阶段,即为每个节点分配一个屏幕坐标。再下一步就是绘制(painting),即遍历render树,并使用UI后端层绘制每一个节点。
这个过程是逐步完成的,为了更好的用户体验,渲染引擎将会尽量早的将内容呈现到屏幕上,并不会等到全部的html都解析完成以后再去构建和布局render树。它是解析完一部份内容就显示一部份内容,同时,可能还在经过网络下载其他内容。
图2.1 Webkit 主流程
图2.2 Mozilla 的 Gecko 渲染引擎主流程
两种渲染引擎的区别:
解析一个文档就是指将这个文档翻译成一个可让代码理解和使用的有意义的结构。获得的结构一般是一个表明了该文档结构的节点树,一般称之为解析树或语法树。
解析是以文档所遵循的语法规则(编写文档所用的语言或格式)为基础的。全部能够解析的格式都必须对应肯定的语法(由词汇和语法规则构成)。这称为与上下文无关的文法。
注意人类语言,HTML都不属于这样的语言,所以没法用常规的解析技术进行解析。
1)解析通常可分为两个子过程
语法分析:对语言应用语法规则。
词法分析:将输入分解为符号,符号是语言的词汇表——基本有效单元的集合。对于人类语言来讲,它至关于咱们字典中出现的全部单词。
2)解析工做通常由两个组件共同完成:
词法分析器(有时也称为标记生成器):负责将输入内容分解成一个个有效标记。词法分析器知道如何将无关的字符(好比空格和换行符)分离出来。
解析器:根据语言的语法规则分析文档的结构,从而构建解析树。
3)解析过程:
解析是一个迭代的过程。一般,解析器会向词法分析器请求一个新标记,并尝试将其与某条语法规则进行匹配。若是发现了匹配规则,解析器会将一个对应于该标记的节点添加到解析树中,而后继续请求下一个标记。
若是没有规则与该标记匹配,解析器就会将标记存储到内部,并继续请求下一个标记,直至找到可与全部内部存储的标记匹配的规则。
若是没有规则(即没有找到相应的语法规则),解析器就会引起一个异常。这意味着文档无效,包含语法错误。
不少时候,解析树还不是最终结果。解析一般是在转换过程当中使用的,而转换是指将输入文档转换成另外一种格式。
编译就是一个例子。编译器可将源代码编译成机器代码,具体过程是首先将源代码解析成解析树,而后将解析树翻译成机器代码文档。
1)词汇一般用正则表达式表示。
2)语法一般使用一种称为 BNF 的格式来定义。
与上下文无关的语法的直观定义就是能够彻底用BNF格式表达的语法。
有两种基本类型的解析器:自上而下解析器和自下而上解析器。直观地来讲,自上而下的解析器从语法的高层结构出发,尝试从中找到匹配的结构。而自下而上的解析器从低层规则出发,将输入内容逐步转化为语法规则,直至知足高层规则。
解析器生成器这个工具能够自动生成解析器,只须要指定语言的文法———词汇表及语法规则,它就能够生成一个解析器。建立一个解析器须要对解析有深刻的理解,并且手动的建立一个有较好性能的解析器并不容易,因此解析生成器颇有用。
Webkit使用两个知名的解析生成器——用于建立语法分析器的Flex及建立解析器的Bison(你可能接触过Lex和Yacc)。Flex的输入是一个包含了符号定义的正则表达式,Bison的输入是用BNF格式表示的语法规则。
HTML 解析器的任务是将 HTML 标记解析成解析树。
正如在解析简介中提到的,上下文无关文法的语法能够用相似BNF的格式来定义。
很遗憾,全部的常规解析器都不适用于 HTML(我并非开玩笑,它们能够用于解析 CSS 和 JavaScript)。HTML 并不能用解析器所需的与上下文无关的语法来定义。
Html有一个正式的格式定义:DTD(Document Type Definition,文档类型定义),但它并非上下文无关的语法。
HTML的定义采用了DTD格式。此格式适用于定义SGML族的语言。它包括全部容许使用的元素及其属性和层次结构的定义。如上文所述,HTML DTD没法构成与上下文无关的语法。
解析器的输出(即”解析树”)是由DOM元素及属性节点组成的。DOM是文档对象模型(Document Object Model) 的缩写。它是HTML文档的对象表示,同时也是外部内容(例如 JavaScript)与HTML元素之间的接口。
解析树的根节点是”Document”对象。DOM与标记之间几乎是一一对应的关系。
这里所说的DOM节点树,指的是那些实现了DOM接口的元素组成的树。
HTML没法用常规的自上而下或自下而上的解析器进行解析。缘由在于:
语言自己的宽容特性;
浏览器对一些常见的非法html有容错机制;
解析过程须要不断地反复。源内容在解析过程当中一般不会改变,可是在HTML中,脚本标记若是包含 “document.write”,就会添加额外的标记,这样解析过程实际上就更改了输入内容。
因为不能使用常规的解析技术,浏览器为html定制了专属的解析器。
HTML5规范详细地描述了解析算法。此算法由两个阶段组成:符号化及构建树。
符号化是词法分析的过程,将输入内容解析成多个标记,HTML标记包括起始标记、结束标记、属性名称和属性值。标记生成器识别标记,传递给树构造器,而后读取下一个字符以识别下一个标记,如此反复直到输入的结束。
该算法的输出结果是HTML标记。该算法使用状态机来表示。每个状态接收来自输入信息流的一个或多个字符,并根据这些字符更新下一个状态。当前的标记化状态和树结构状态会影响进入下一状态的决定。这意味着,即便接收的字符相同,对于下一个正确的状态也会产生不一样的结果,具体取决于当前的状态。
在建立解析器的同时,也会建立 Document 对象。在树构建阶段,以 Document 为根节点的 DOM 树也会不断进行修改,向其中添加各类元素。标记生成器发送的每一个节点都会由树构建器进行处理。规范中定义了每一个标记所对应的 DOM 元素,这些元素会在接收到相应的标记时建立。这些元素不只会添加到 DOM 树中,还会添加到开放元素的堆栈中。此堆栈用于纠正嵌套错误和处理未关闭的标记。其算法也能够用状态机来描述。这些状态称为“插入模式”。
树构建阶段的输入是一个来自标记化阶段的标记序列。第一个模式是“initial mode”。接收 HTML 标记后转为“before html”模式,并在这个模式下从新处理此标记。这样会建立一个 HTMLHtmlElement 元素,并将其附加到 Document 根对象上。
而后状态将改成“before head”。此时咱们接收“body”标记。即便咱们的示例中没有“head”标记,系统也会隐式建立一个 HTMLHeadElement,并将其添加到树中。
如今咱们进入了“in head”模式,而后转入“after head”模式。系统对 body 标记进行从新处理,建立并插入 HTMLBodyElement,同时模式转变为“body”。
如今,接收由“Hello world”字符串生成的一系列字符标记。接收第一个字符时会建立并插入“Text”节点,而其余字符也将附加到该节点。
接收 body 结束标记会触发“after body”模式。如今咱们将接收 HTML 结束标记,而后进入“after after body”模式。接收到文件结束标记后,解析过程就此结束。
在此阶段,浏览器会将文档标注为交互状态,并开始解析那些处于“deferred”模式的脚本,也就是那些应在文档解析完成后才执行的脚本。而后,文档状态将设置为“完成”,一个“加载”事件将随之触发。
至少要可以处理如下错误状况:
明显不能在某些外部标记中添加的元素。在此状况下,咱们应该关闭全部标记,直到出现禁止添加的元素,而后再加入该元素。
咱们不能直接添加的元素。这极可能是网页做者忘记添加了其中的一些标记(或者其中的标记是可选的)。这些标签可能包括:HTML HEAD BODY TBODY TR TD LI(还有遗漏的吗?)。
向 inline 元素内添加 block 元素。关闭全部 inline 元素,直到出现下一个较高级的 block 元素。
若是这样仍然无效,可关闭全部元素,直到能够添加元素为止,或者忽略该标记。
和HTML不一样,CSS是上下文无关的语法,可使用简介中描述的各类解析器进行解析。事实上,CSS 规范定义了 CSS 的词法和语法。
Webkit 使用 Flex 和 Bison 解析器生成器,经过 CSS 语法文件自动建立解析器。正如咱们以前在解析器简介中所说,Bison 会建立自下而上的移位归约解析器。Firefox 使用的是人工编写的自上而下的解析器。这两种解析器都会将 CSS 文件解析成 StyleSheet 对象,且每一个对象都包含 CSS 规则。CSS 规则对象则包含选择器和声明对象,以及其余与 CSS 语法对应的对象。
网络的模型是同步的。网页做者但愿解析器遇到 <script>
标记时当即解析并执行脚本。文档的解析将中止,直到脚本执行完毕。若是脚本是外部的,那么解析过程会中止,直到从网络同步抓取资源完成后再继续。此模型已经使用了多年,也在 HTML4 和 HTML5 规范中进行了指定。做者也能够将脚本标注为“defer”,这样它就不会中止文档解析,而是等到解析结束才执行。HTML5 增长了一个选项,可将脚本标记为异步,以便由其余线程解析和执行。
Webkit 和 Firefox 都进行了这项优化。在执行脚本时,其余线程会解析文档的其他部分,找出并加载须要经过网络加载的其余资源。经过这种方式,资源能够在并行链接上加载,从而提升整体速度。请注意,预解析器不会修改 DOM 树,而是将这项工做交由主解析器处理;预解析器只会解析外部资源(例如外部脚本、样式表和图片)的引用。
另外一方面,样式表有着不一样的模型。理论上来讲,应用样式表不会更改 DOM 树,所以彷佛没有必要等待样式表并中止文档解析。但这涉及到一个问题,就是脚本在文档解析阶段会请求样式信息。若是当时尚未加载和解析样式,脚本就会得到错误的回复,这样显然会产生不少问题。这看上去是一个非典型案例,但事实上很是广泛。Firefox 在样式表加载和解析的过程当中,会禁止全部脚本。而对于 Webkit 而言,仅当脚本尝试访问的样式属性可能受还没有加载的样式表影响时,它才会禁止该脚本。
在 DOM 树构建的同时,浏览器还会构建另外一个树结构:渲染树。这是由可视化元素按照其显示顺序而组成的树,也是文档的可视化表示。它的做用是让您按照正确的顺序绘制内容。
Firefox 将渲染树中的元素称为“框架”。Webkit 使用的术语是呈现器或呈现对象。
呈现器知道如何布局并将自身及其子元素绘制出来。
呈现器是和 DOM 元素相对应的,但并不是一一对应。非可视化的 DOM 元素不会插入渲染树中,例如“head”元素。若是元素的 display 属性值为“none”,那么也不会显示在渲染树中(可是 visibility 属性值为“hidden”的元素仍会显示)。
有一些 DOM 元素对应多个可视化对象。它们每每是具备复杂结构的元素,没法用单一的矩形来描述。例如,“select”元素有 3 个呈现器:一个用于显示区域,一个用于下拉列表框,还有一个用于按钮。若是因为宽度不够,文本没法在一行中显示而分为多行,那么新的行也会做为新的呈现器而添加。
另外一个关于多呈现器的例子是格式无效的 HTML。根据 CSS 规范,inline 元素只能包含 block 元素或 inline 元素中的一种。若是出现了混合内容,则应建立匿名的 block 呈现器,以包裹 inline 元素。
有一些呈现对象对应于 DOM 节点,但在树中所在的位置与 DOM 节点不一样。浮动定位和绝对定位的元素就是这样,它们处于正常的流程以外,放置在树中的其余地方,并映射到真正的框架,而放在原位的是占位框架。
初始容器 block 为“viewport”,而在 Webkit 中则为“RenderView”对象。
在 Firefox 中,系统会针对 DOM 更新注册展现层,做为侦听器。展现层将框架建立工做委托给 FrameConstructor,由该构造器解析样式(请参阅样式计算)并建立框架。
在 Webkit 中,解析样式和建立呈现器的过程称为“附加”。每一个 DOM 节点都有一个“attach”方法。附加是同步进行的,将节点插入 DOM 树须要调用新的节点“attach”方法。
处理 html 和 body 标记就会构建渲染树根节点。这个根节点呈现对象对应于 CSS 规范中所说的容器 block,这是最上层的 block,包含了其余全部 block。它的尺寸就是视口,即浏览器窗口显示区域的尺寸。Firefox 称之为 ViewPortFrame,而 Webkit 称之为 RenderView。这就是文档所指向的呈现对象。渲染树的其他部分以 DOM 树节点插入的形式来构建。
请参阅关于处理模型的 CSS2 规范。
构建渲染树时,须要计算每个呈现对象的可视化属性。这是经过计算每一个元素的样式属性来完成的。
样式包括来自各类来源的样式表、inline 样式元素和 HTML 中的可视化属性(例如“bgcolor”属性)。其中后者将通过转化以匹配 CSS 样式属性。
样式表的来源包括浏览器的默认样式表、由网页做者提供的样式表以及由浏览器用户提供的用户样式表(浏览器容许您定义本身喜欢的样式。以 Firefox 为例,用户能够将本身喜欢的样式表放在“Firefox Profile”文件夹下)。
样式计算存在如下难点:
样式数据是一个超大的结构,存储了无数的样式属性,这可能形成内存问题。
若是不进行优化,为每个元素查找匹配的规则会形成性能问题。要为每个元素遍历整个规则列表来寻找匹配规则,这是一项浩大的工程。选择器会具备很复杂的结构,这就会致使某个匹配过程一开始看起来极可能是正确的,但最终发现实际上是徒劳的,必须尝试其余匹配路径。
让咱们来看看浏览器是如何处理这些问题的:
Webkit 节点会引用样式对象 (RenderStyle)。这些对象在某些状况下能够由不一样节点共享。这些节点是同级关系,而且:
这些元素必须处于相同的鼠标状态(例如,不容许其中一个是“:hover”状态,而另外一个不是)
任何元素都没有 ID
标记名称应匹配
类属性应匹配
映射属性的集合必须是彻底相同的
连接状态必须匹配
焦点状态必须匹配
任何元素都不该受属性选择器的影响,这里所说的“影响”是指在选择器中的任何位置有任何使用了属性选择器的选择器匹配
元素中不能有任何 inline 样式属性
不能使用任何同级选择器。WebCore 在遇到任何同级选择器时,只会引起一个全局开关,并停用整个文档的样式共享(若是存在)。这包括 + 选择器以及 :first-child 和 :last-child 等选择器。
为了简化样式计算,Firefox 还采用了另外两种树:规则树和样式上下文树。Webkit 也有样式对象,但它们不是保存在相似样式上下文树这样的树结构中,只是由 DOM 节点指向此类对象的相关样式。
样式上下文包含端值。要计算出这些值,应按照正确顺序应用全部的匹配规则,并将其从逻辑值转化为具体的值。例如,若是逻辑值是屏幕大小的百分比,则须要换算成绝对的单位。规则树的点子真的很巧妙,它使得节点之间能够共享这些值,以免重复计算,还能够节约空间。
全部匹配的规则都存储在树中。路径中的底层节点拥有较高的优先级。规则树包含了全部已知规则匹配的路径。规则的存储是延迟进行的。规则树不会在开始的时候就为全部的节点进行计算,而是只有当某个节点样式须要进行计算时,才会向规则树添加计算的路径。
这个想法至关于将规则树路径视为词典中的单词。若是咱们已经计算出以下的规则树:
假设咱们须要为内容树中的另外一个元素匹配规则,而且找到匹配路径是 B - E - I(按照此顺序)。因为咱们在树中已经计算出了路径 A - B - E - I - L,所以就已经有了此路径,这就减小了如今所需的工做量。
让咱们看看规则树如何帮助咱们减小工做。
1)结构划分
样式上下文可分割成多个结构。这些结构体包含了特定类别(如 border 或 color)的样式信息。结构中的属性都是继承的或非继承的。继承属性若是未由元素定义,则继承自其父代。非继承属性(也称为“重置”属性)若是未进行定义,则使用默认值。
规则树经过缓存整个结构(包含计算出的端值)为咱们提供帮助。这一想法假定底层节点没有提供结构的定义,则可以使用上层节点中的缓存结构。
2)使用规则树计算样式上下文
在计算某个特定元素的样式上下文时,咱们首先计算规则树中的对应路径,或者使用现有的路径。而后咱们沿此路径应用规则,在新的样式上下文中填充结构。咱们从路径中拥有最高优先级的底层节点(一般也是最特殊的选择器)开始,并向上遍历规则树,直到结构填充完毕。若是该规则节点对于此结构没有任何规范,那么咱们能够实现更好的优化:寻找路径更上层的节点,找到后指定完整的规范并指向相关节点便可。这是最好的优化方法,由于整个结构都能共享。这能够减小端值的计算量并节约内存。
若是咱们找到了部分定义,就会向上遍历规则树,直到结构填充完毕。
若是咱们找不到结构的任何定义,那么假如该结构是“继承”类型,咱们会在上下文树中指向父代的结构,这样也能够共享结构。若是是 reset 类型的结构,则会使用默认值。
若是最特殊的节点确实添加了值,那么咱们须要另外进行一些计算,以便将这些值转化成实际值。而后咱们将结果缓存在树节点中,供子代使用。
若是某个元素与其同级元素都指向同一个树节点,那么它们就能够共享整个样式上下文。
在 Webkit 中没有规则树,所以会对匹配的声明遍历 4 次。首先应用非重要高优先级的属性(因为做为其余属性的依据而应首先应用的属性,例如 display),接着是高优先级重要规则,而后是普通优先级非重要规则,最后是普通优先级重要规则。这意味着屡次出现的属性会根据正确的层叠顺序进行解析。最后出现的最终生效。
所以归纳来讲,共享样式对象(整个对象或者对象中的部分结构)能够解决问题 1和问题 3。Firefox 规则树还有助于按照正确的顺序应用属性。
样式对象具备每一个可视化属性一一对应的属性(均为 CSS 属性但更为通用)。若是某个属性未由任何匹配规则所定义,那么部分属性就可由父代元素样式对象继承。其余属性具备默认值。
若是定义不止一个,就会出现问题,须要经过层叠顺序来解决。
1)样式表层叠顺序
某个样式属性的声明可能会出如今多个样式表中,也可能在同一个样式表中出现屡次。这意味着应用规则的顺序极为重要。这称为“层叠”顺序。根据 CSS2 规范,层叠的顺序为(优先级从低到高):
浏览器声明
用户普通声明
做者普通声明
做者重要声明
用户重要声明
浏览器声明是重要程度最低的,而用户只有将该声明标记为“重要”才能够替换网页做者的声明。一样顺序的声明会根据特异性进行排序,而后再是其指定顺序。HTML 可视化属性会转换成匹配的 CSS 声明。它们被视为低优先级的网页做者规则。
2)特异性
选择器的特异性由 CSS2 规范定义以下:
若是声明来自于“style”属性,而不是带有选择器的规则,则记为 1,不然记为 0 (= a)
记为选择器中 ID 属性的个数 (= b)
记为选择器中其余属性和伪类的个数 (= c)
记为选择器中元素名称和伪元素的个数 (= d)
将四个数字按 a-b-c-d 这样链接起来(位于大数进制的数字系统中),构成特异性。
您使用的进制取决于上述类别中的最高计数。
例如,若是 a=14,您可使用十六进制。若是 a=17,那么您须要使用十七进制;固然不太可能出现这种状况,除非是存在以下的选择器:html body div div p …(在选择器中出现了 17 个标记,这样的可能性极低)。
3)规则排序
找到匹配的规则以后,应根据级联顺序将其排序。Webkit 对于较小的列表会使用冒泡排序,而对较大的列表则使用归并排序。
Webkit 使用一个标记来表示是否全部的顶级样式表(包括 @imports)均已加载完毕。若是在附加过程当中还没有彻底加载样式,则使用占位符,并在文档中进行标注,等样式表加载完毕后再从新计算。
呈现器在建立完成并添加到渲染树时,并不包含位置和大小信息。计算这些值的过程称为布局或重排。
HTML 采用基于流的布局模型,这意味着大多数状况下只要一次遍历就能计算出几何信息。处于流中靠后位置元素一般不会影响靠前位置元素的几何特征,所以布局能够按从左至右、从上至下的顺序遍历文档。可是也有例外状况,好比 HTML 表格的计算就须要不止一次的遍历 。
坐标系是相对于根框架而创建的,使用的是上坐标和左坐标。
布局是一个递归的过程。它从根呈现器(对应于 HTML 文档的 元素)开始,而后递归遍历部分或全部的框架层次结构,为每个须要计算的呈现器计算几何信息。
根呈现器的位置左边是 0,0,其尺寸为视口(也就是浏览器窗口的可见区域)。
全部的呈现器都有一个“laybout”或者“reflow”方法,每个呈现器都会调用其须要进行布局的子代的 layout 方法。
为避免对全部细小更改都进行总体布局,浏览器采用了一种“dirty 位”系统。若是某个呈现器发生了更改,或者将自身及其子代标注为“dirty”,则须要进行布局。
有两种标记:“dirty”和“children are dirty”。“children are dirty”表示尽管呈现器自身没有变化,但它至少有一个子代须要布局。
全局布局是指触发了整个渲染树范围的布局,触发缘由可能包括:
影响全部呈现器的全局样式更改,例如字体大小更改。
屏幕大小调整。
布局能够采用增量方式,也就是只对 dirty 呈现器进行布局(这样可能存在须要进行额外布局的弊端)。
当呈现器为 dirty 时,会异步触发增量布局。例如,当来自网络的额外内容添加到 DOM 树以后,新的呈现器附加到了渲染树中。
增量布局是异步执行的。Firefox 将增量布局的“reflow 命令”加入队列,而调度程序会触发这些命令的批量执行。Webkit 也有用于执行增量布局的计时器:对渲染树进行遍历,并对 dirty 呈现器进行布局。
请求样式信息(例如“offsetHeight”)的脚本可同步触发增量布局。
全局布局每每是同步触发的。
有时,当初始布局完成以后,若是一些属性(如滚动位置)发生变化,布局就会做为回调而触发。
若是布局是由“大小调整”或呈现器的位置(而非大小)改变而触发的,那么能够从缓存中获取呈现器的大小,而无需从新计算。
在某些状况下,只有一个子树进行了修改,所以无需从根节点开始布局。这适用于在本地进行更改而不影响周围元素的状况,例如在文本字段中插入文本(不然每次键盘输入都将触发从根节点开始的布局)。
布局一般具备如下模式:
父呈现器肯定本身的宽度。
父呈现器依次处理子呈现器,而且:
放置子呈现器(设置 x,y 坐标)。
若是有必要,调用子呈现器的布局(若是子呈现器是 dirty 的,或者这是全局布局,或出于其余某些缘由),这会计算子呈现器的高度。
父呈现器根据子呈现器的累加高度以及边距和补白的高度来设置自身高度,此值也可供父呈现器的父呈现器使用。
将其 dirty 位设置为 false。
Firefox 使用“state”对象 (nsHTMLReflowState) 做为布局的参数(称为“reflow”),这其中包括了父呈现器的宽度。
Firefox 布局的输出为“metrics”对象 (nsHTMLReflowMetrics),其包含计算得出的呈现器高度。
若是呈现器在布局过程当中须要换行,会当即中止布局,并告知其父代须要换行。父代会建立额外的呈现器,并对其调用布局。
在绘制阶段,系统会遍历渲染树,并调用呈现器的“paint”方法,将呈现器的内容显示在屏幕上。绘制工做是使用用户界面基础组件完成的。
和布局同样,绘制也分为全局(绘制整个渲染树)和增量两种。在增量绘制中,部分呈现器发生了更改,可是不会影响整个树。更改后的呈现器将其在屏幕上对应的矩形区域设为无效,这致使 OS 将其视为一块“dirty 区域”,并生成“paint”事件。OS 会很巧妙地将多个区域合并成一个。在 Chrome 浏览器中,状况要更复杂一些,由于 Chrome 浏览器的呈现器不在主进程上。Chrome 浏览器会在某种程度上模拟 OS 的行为。展现层会侦听这些事件,并将消息委托给呈现根节点。而后遍历渲染树,直到找到相关的呈现器,该呈现器会从新绘制本身(一般也包括其子代)。
Firefox 遍历整个渲染树,为绘制的矩形创建一个显示列表。列表中按照正确的绘制顺序(先是呈现器的背景,而后是边框等等)包含了与矩形相关的呈现器。这样等到从新绘制的时候,只需遍历一次渲染树,而不用屡次遍历(绘制全部背景,而后绘制全部图片,再绘制全部边框等等)。
Firefox 对此过程进行了优化,也就是不添加隐藏的元素,例如被不透明元素彻底遮挡住的元素。
在从新绘制以前,Webkit 会将原来的矩形另存为一张位图,而后只绘制新旧矩形之间的差别部分。
在发生变化时,浏览器会尽量作出最小的响应。所以,元素的颜色改变后,只会对该元素进行重绘。元素的位置改变后,只会对该元素及其子元素(可能还有同级元素)进行布局和重绘。添加 DOM 节点后,会对该节点进行布局和重绘。一些重大变化(例如增大“html”元素的字体)会致使缓存无效,使得整个渲染树都会进行从新布局和绘制。
渲染引擎采用了单线程。几乎全部操做(除了网络操做)都是在单线程中进行的。在 Firefox 和 Safari 中,该线程就是浏览器的主线程。而在 Chrome 浏览器中,该线程是标签进程的主线程。
网络操做可由多个并行线程执行。并行链接数是有限的(一般为 2 至 6 个,以 Firefox 3 为例是 6 个)。
事件循环
浏览器的主线程是事件循环。它是一个无限循环,永远处于接受处理状态,并等待事件(如布局和绘制事件)发生,并进行处理。
根据 CSS2 规范,“画布”这一术语是指“用来呈现格式化结构的空间”,也就是供浏览器绘制内容的区域。画布的空间尺寸大小是无限的,可是浏览器会根据视口的尺寸选择一个初始宽度。
根据 www.w3.org/TR/CSS2/zindex.html,画布若是包含在其余画布内,就是透明的;不然会由浏览器指定一种颜色。
CSS 框模型描述的是针对文档树中的元素而生成,并根据可视化格式模型进行布局的矩形框。
每一个框都有一个内容区域(例如文本、图片等),还有可选的周围补白、边框和边距区域。
每个节点都会生成 0..n 个这样的框。 全部元素都有一个“display”属性,决定了它们所对应生成的框类型。
默认值是 inline,可是浏览器样式表设置了其余默认值。例如,“div”元素的 display 属性默认值是 block。
您能够在这里找到默认样式表示例:www.w3.org/TR/CSS2/sample.html
有三种定位方案:
普通:根据对象在文档中的位置进行定位,也就是说对象在渲染树中的位置和它在 DOM 树中的位置类似,并根据其框类型和尺寸进行布局。
浮动:对象先按照普通流进行布局,而后尽量地向左或向右移动。
绝对:对象在渲染树中的位置和它在 DOM 树中的位置不一样。
定位方案是由“position”属性和“loat”属性设置的。
若是值是 static 和 relative,就是普通流
若是值是 absolute 和 fixed,就是绝对定位
static 定位无需定义位置,而是使用默认定位。对于其余方案,网页做者须要指定位置:top、bottom、left、right。
框的布局方式是由如下因素决定的:
框尺寸
定位方案
外部信息,例如图片大小和屏幕大小
框类型
block 框:造成一个 block,在浏览器窗口中拥有其本身的矩形区域。
block 采用的是一个接一个的垂直格式,而 inline 采用的是水平格式。
inline 框放置在行中或“行框”中。这些行至少和最高的框同样高,还能够更高,当框根据“底线”对齐时,这意味着元素的底部须要根据其余框中非底部的位置对齐。若是容器的宽度不够,inline 元素就会分为多行放置。在段落中常常发生这种状况。
相对定位:先按照普通方式定位,而后根据所需偏移量进行移动。
浮动框会移动到行的左边或右边。有趣的特征在于,其余框会浮动在它的周围。
这种布局是准肯定义的,与普通流无关。元素不参与普通流。尺寸是相对于容器而言的。在固定定位中,容器就是可视区域。
请注意,即便在文档滚动时,固定框也不会移动。
这是由 z-index CSS 属性指定的。它表明了框的第三个维度,也就是沿“z 轴”方向的位置。
这些框分散到多个堆栈(称为堆栈上下文)中。在每个堆栈中,会首先绘制后面的元素,而后在顶部绘制前面的元素,以便更靠近用户。若是出现重叠,新绘制的元素就会覆盖以前的元素。
堆栈是按照 z-index 属性进行排序的。具备“z-index”属性的框造成了本地堆栈。视口具备外部堆栈。
关键词:
HTML: 超文本标记语言。“超文本”就是指页面内能够包含图片、连接,甚至音乐、程序等非文字元素。
XML:可扩展标记语言。一种用于标记电子文件使其具备结构性的标记语言。
PDF:Portable Document Format的简称,意为 便携式文档格式。
正则表达式(规则表达式):Regular Expression,简写为regex、regexp或RE,计算机科学的一个概念。正则表一般被用来检索、替换那些符合某个模式(规则)的文本。
BNF:Backus-Naur Form,意为巴科斯范式,一种形式化符号来描述给定语言的语法。
DOM:Document Object Model,意为文档对象模型,处理可扩展标志语言的标准编程接口。
脚本:是使用一种特定的描述性语言,依据必定的格式编写的可执行文件,又称做宏或批处理文件。
参考:http://blog.csdn.net/dangnian/article/details/50876241