本文翻译自http://taligarsiel.com/Projects/howbrowserswork1.htm
注:原文有些罗嗦的内容直接隐去了,有兴趣的可以阅读原文。
还有一篇翻译过的:http://ux.sohu.com/topics/50972d9ae7de3e752e0081ff。 我想再翻译一遍,原因是,一是为了学习,翻译是个不错的途径;二是,也加入一些自己的理解。
介绍
浏览器基本结构
图1. 浏览器主组件
Chrome浏览器与其他浏览器不同,它是渲染引擎多进程的,每个网页在分离的进程中渲染。一个标签一个进程(虽然实际情况并不是这样,但是基本上可以这么理解)。
渲染引擎(The rendering engine)
渲染引擎,自然是用来渲染了(废话)。 默认情况下,渲染引擎可以显示HTML, XML文档和图片。也可以通过插件显示别的内容。例如PDF。本章不讨论插件,重点讨论HTNMl和图片的渲染。
各浏览器的渲染引擎
Firfox使用Gecko-Mozilia官方引擎。Safari和Chrome都使用Webkit。
主流程
(解析HTML以构造DOM-> 渲染树构造 -> 布局渲染树 -> 绘制渲染树)
渲染引擎首先将解析HTML,将标签转换位DOM的节点树,这颗树成为”内容树“。同时还要解析风格,包括外部的CSS文件和内部的style元素。
这些风格定义信息和HTML的可见元素将床另外一棵树:渲染树。
渲染树包括矩形区域信息和可见属性,如颜色和位置。矩形区域将以正确的次序显示在屏幕上。
然后,渲染引擎进入布局过程。此过程将计算每个节点在屏幕上正确的坐标。然后是绘制,遍历渲染树上每个节点并绘制到UI backed层。
为了提升用户体验,渲染引擎会尽力尽早的显示出内容。不会等到HTML下载完成并解析和布局后。
主流程实例
Webkit的主流程:
Mozilla Gecko渲染引擎的主流程:
上两图说明Webkit和Gecko使用的技术明显不同,但是基本流程一样。
Gecko将可见元素组成的树为Frame树。每个元素都是一个frame。wekbit则使用"Render Tree"这个概念,它由"Render Objects"。Webkit使用"layout"描述元素定位,而Gecko则使用"Reflow"。 Webkit将DOM节点创建对应的"Render Tree"节点的过程为“Attachment"。
除此之外,Gecko在HTML和DOM树增加了一个额外层--“content sink",它是创建DOM元素的类工程。
HTML解析器
HTML解析器的功能是将HTML源码解析为一个解析树。
DOM
<html>
<body>
<p>
Hello World
</p>
<div> <img src="example.png"/></div>
</body>
</html>
将被转换为

解析算法

tokenization 算法
<html>
<body>
Hello world
</body>
</html>
初始状态为 "Data state", 当 '<' 字符到来时,状态变为"Tag open state" (标签开入状态)。 收到"a-z"字符时,会创建一个"开始标签 token", 状态也会变为 "Tag name state",直到收到">"字符。 这个状态下每个字符都会添加到token name中。在我们的例子中,我们创建了一个"html" token。

树构造算法
<html>
<body>
Hello world
</body>
</html>
在树构造阶段,输入的是产生于tokenization阶段的token序列。 初始模式为“initial mode",收到html token将进入"before html"模式,并重新处理该token。 此时将引起一个HTMLHtmlElement源被创建并设置为Document对象的根元素。

解析完成后的动作
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 (用'#'开头)
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*] ')' ]
;
其中,ruleset是这样一个结构
div.error , a.error {
color:red;
font-weight:bold;
}
div.error 和 a.error 是选择器(selector)。括号内部分,包含了一组规则(rule). 它的正式定义是
ruleset
: selector [ ',' S* selector ]*
'{' S* declaration [ ';' S* declaration ]* '}' S*
;
它表示,ruleset由一个selector或者多个可选的以空格或者逗号分割的多个selector组成,一个ruleset包含一对大括号,括号内是一个或者多个用分号分割的声明。 声明和选择器被后面的BNF文法所定义。
Webkit CSS解析器

脚本解析
处理脚本和样式单的顺序
预测解析
样式表
渲染树的构造
class RenderObject{
virtual void layout();
virtual void paint(PaintInfo);
virtual void rect repaintRect();
Node* node; //the DOM node
RenderStyle* style; // the computed style
RenderLayer* containgLayer; //the containing z-index layer
}
每个渲染元素代表一个矩形区域,这个区域被 称为CSS box,在CSS2标准中被定义。
RenderObject* RenderObject::createObject(Node* node, RenderStyle* style)
{
Document* doc = node->document();
RenderArena* arena = doc->renderArena();
...
RenderObject* o = 0;
switch (style->display()) {
case NONE:
break;
case INLINE:
o = new (arena) RenderInline(node);
break;
case BLOCK:
o = new (arena) RenderBlock(node);
break;
case INLINE_BLOCK:
o = new (arena) RenderBlock(node);
break;
case LIST_ITEM:
o = new (arena) RenderListItem(node);
break;
...
}
return o;
}
元素的类型也是考虑因素之一。如,form和table就拥有特殊的类型。
渲染树关联到DOM树

渲染树构造流
样式计算
div div div div{
...
}
这个规则将匹配有3个“div"祖先的div元素。嘉定你想检查一个给定的div元素是否符合这个规则,就得向上一个一个的检验。
共享样式数据
在某些情况下,webkit多个节点会共享同一个样式对象(RenderStyle)。这些节点一般是兄弟或者堂兄弟节点,或者是:firefox的规则树



结构分离
使用规则树来计算样式上下文
<html>
<body>
<div class="err" id="div1">
<p>
this is a <span class="big"> big error </span>
this is also a
<span class="big"> very big error</span> error
</p>
</div>
<div class="err" id="div2">another error</div>
</body>
</html>
有如下样式规则
1. div {margin:5px;color:black}
2. .err {color:red}
3. .big {margin-top:3px}
4. div span {margin-bottom:4px}
5. #div1 {color:blue}
6. #div 2 {color:green}
firefox将生成规则树( A,B,C,D等表示节点的名称,是为了后文记述方便,实际不存在,数字表示第几个规则)


p {font-family:Verdana;font size:10px;font-weight:bold}
作为p的子元素的span元素,将继承p定义的字体风格。(color属性实际上也是继承的,但是firefox将它看做不可继承属性对象)。
维护规则以便简化匹配过程
- CSS样式表,包括外部样式表文件和style元素定义;
p {color:blue}
- 内联样式属性
<p style="color:blue" />
- HTML的可见属性
<p bgcolor="blue" />
后两种非常容易处理,因为他们只对特定元素有效。
p.error {color:red} #messageDiv {heigh
第一个规则,将被插入到class表。第二个将被插入到id表,第三个将被插入到标签表。div {margin:5px}
<p class="error">an error occurred </p>
<div id=" messageDiv">this is a message</div>
首先,试图查找匹配p元素的规则。class表中包含“error”key的"p.error"规则被发现。
table div {margin:5px}
他讲被加入到标签表中,以最右边的选择器为key(div)。但是对于上面的HTML代码,他将不会被选中,因为我们的div没有一个“table"祖先元素。
以正确的顺序匹配规则
样式的级联次序
特殊性
- 如果直接定义在元素的style属性,那么,记为 a= 1, 否则记为a=0
- 计算选择器中ID的数量,记为b的值
- 计算选择器中的其他属性和伪类的数量,记为c的值
- 计算选择器中的元素名称和伪元素的数量,记为d
* {} /* a=0 b=0 c=0 d=0 -> specificity = 0,0,0,0 */
li {} /* a=0 b=0 c=0 d=1 -> specificity = 0,0,0,1 */
li:first-line {} /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */
ul li {} /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */
ul ol+li {} /* a=0 b=0 c=0 d=3 -> specificity = 0,0,0,3 */
h1 + *[rel=up]{} /* a=0 b=0 c=1 d=1 -> specificity = 0,0,1,1 */
ul ol li.red {} /* a=0 b=0 c=1 d=3 -> specificity = 0,0,1,3 */
li.red.level {} /* a=0 b=0 c=2 d=1 -> specificity = 0,0,2,1 */
#x34y {} /* a=0 b=1 c=0 d=0 -> specificity = 0,1,0,0 */
style="" /* a=1 b=0 c=0 d=0 -> specificity = 1,0,0,0 */
对规则进行排序
static bool operator >(CSSRuleData& r1, CSSRuleData& r2)
{
int spec1 = r1.selector()->specificity();
int spec2 = r2.selector()->specificity();
return (spec1 == spec2) : r1.position() > r2.position() : spec1 > spec2;
}
平滑处理
布局
脏标志系统
全局和自增布局

同步和异步布局
优化
布局过程
- 父渲染元素决定它自己的宽度;
- 父元素遍历子元素,并
- 确定子元素的位置(x,y)
- 如果需要,调用元素的布局,最终得到子元素的高度
- 父对象使用子元素的总体高度以及它自己的margins和paddings的高度,作为它自己的高度
- 清除脏标记
宽度计算
<div style="width:30%"/>
- webkit的计算过程(class RenderBox的calcWidth方法)
- 计算容器允许的最大宽度。本例中的允许宽度是内容的宽度: clientWidth() - paddingLeft() - paddingRight() clientWidth和clientHeight代表了一个对象除了边框和滚动条外的内部值。
- 根据元素的width属性,计算出容器的30%
- 添加水平边框和paddings的值
断行
绘制
全局和自增绘制
绘制顺序
- 背景色
- 背景图片
- 边框
- 子对象
- 轮廓
Firefox显示列表
webkit的矩形存储
动态变化
渲染引擎线程
事件循环
while (!mExiting)
NS_ProcessNextEvent(thread);
CSS2可视模型
Canvas
CSS Box模型

定位模式
- static和relative标记一个普通定位额
- absolute和fixed标记一个觉得定位
- box的类型
- box的大小
- 定位模式
- 外部信息-如图片大小和屏幕大小等
box类型



定位
相对定位

浮动定位
<p>
<img style="float:right" src="images/image.gif" width="100" height="100">Lorem ipsum dolor sit amet, consectetuer...
</p>
看起来就是:

决定和固定定位

层
<STYLE type="text/css">
div {
position: absolute;
left: 2in;
top: 2in;
}
</STYLE>
<P>
<DIV
style="z-index: 3;background-color:red; width: 1in; height: 1in; ">
</DIV>
<DIV
style="z-index: 1;background-color:green;width: 2in; height: 2in;">
</DIV>
</p>
结果是

虽然绿色div后于红色div被定义,但是由于z-index的关系,红色div覆盖掉了绿色div的区域。
参考文献
- Browser architecture
- Grosskurth, Alan. A Reference Architecture for Web Browsers. http://grosskurth.ca/papers/browser-refarch.pdf.
- Parsing
- Aho, Sethi, Ullman, Compilers: Principles, Techniques, and Tools (aka the "Dragon book"), Addison-Wesley, 1986
- Rick Jelliffe. The Bold and the Beautiful: two new drafts for HTML 5. http://broadcast.oreilly.com/2009/05/the-bold-and-the-beautiful-two.html.
- Firefox
- L. David Baron, Faster HTML and CSS: Layout Engine Internals for Web Developers. http://dbaron.org/talks/2008-11-12-faster-html-and-css/slide-6.xhtml.
- L. David Baron, Faster HTML and CSS: Layout Engine Internals for Web Developers(Google tech talk video). http://www.youtube.com/watch?v=a2_6bGNZ7bA.
- L. David Baron, Mozilla's Layout Engine. http://www.mozilla.org/newlayout/doc/layout-2006-07-12/slide-6.xhtml.
- L. David Baron, Mozilla Style System Documentation. http://www.mozilla.org/newlayout/doc/style-system.html.
- Chris Waterson, Notes on HTML Reflow. http://www.mozilla.org/newlayout/doc/reflow.html.
- Chris Waterson, Gecko Overview. http://www.mozilla.org/newlayout/doc/gecko-overview.htm.
- Alexander Larsson, The life of an HTML HTTP request. https://developer.mozilla.org/en/The_life_of_an_HTML_HTTP_request.
- Webkit
- David Hyatt, Implementing CSS(part 1). http://weblogs.mozillazine.org/hyatt/archives/cat_safari.html.
- David Hyatt, An Overview of WebCore. http://weblogs.mozillazine.org/hyatt/WebCore/chapter2.html.
- David Hyatt, WebCore Rendering. http://webkit.org/blog/114/.
- David Hyatt, The FOUC Problem. http://webkit.org/blog/66/the-fouc-problem/.
- W3C Specifications
- HTML 4.01 Specification. http://www.w3.org/TR/html4/.
- HTML5 Specification. http://dev.w3.org/html5/spec/Overview.html.
- Cascading Style Sheets Level 2 Revision 1 (CSS 2.1) Specification. http://www.w3.org/TR/CSS2/.
- Browsers build instructions
- Firefox. https://developer.mozilla.org/en/Build_Documentation
- Webkit. http://webkit.org/building/build.html