(转载自: http://www.cnblogs.com/jz1108/archive/2012/07/02/2573045.html)
文档树
网页会被解析成一个包含若干结点的树形结构,即文档对象模型(DOM)。树中所有结点的基类为Node。
Node.h
结点被划分为若干类型。与渲染代码相关的结点类型包括:
- Document(文档类):树的根节点总为文档。有三个文档相关的类:Document、HTMLDocument和SVGDocument。第一个用来表示除SVG文档以外的所有XML文档。第二个继承自Document,只表示HTML文档。第三个也继承自Document,用来表示SVG文档(涉及:Document.h、HTMLDocument.h)。
- Elements(元素类):在HTML或XML代码中的标签最终会变为元素。从渲染角度看,元素是一个具有标记名的结点,可用来在渲染时转换为某个特定的子类,从而获取其数据。(涉及:Element.h)
- Text(文本):元素之间的纯文本会被转化为一个文本结点。文本结点负责存储纯文本信息,渲染树可通过它来获取数据(涉及:Text.h)。
渲染树
渲染树是渲染过程的核心。渲染树和DOM树很像,都是树形结构,树中的每个元素可对应到文档、元素或者文本结点。渲染树还能包含一些DOM树中没有的结点。
渲染树结点的基类为RenderObject。
RenderObject.h
DOM结点所对应的RenderObject对象可通过Node的renderer()方法获得。
RenderObject* renderer() const
以下方法是遍历渲染树最常用的:
RenderObject* firstChild() const; RenderObject* lastChild() const; RenderObject* previousSibling() const; RenderObject* nextSibling() const;
下面的例子我们循环访问了一个渲染结点的子结点。这在渲染树的代码中很常见。
for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { ... }
创建渲染树
渲染结点通过一个叫做attachment的过程创建出来。随着文档被解析、DOM结点被添加,一个叫做attach的方法会被DOM结点调用来创建每个渲染结点。
void attach()
attach方法为DOM结点计算样式信息。如果元素CSS的display属性为none,或者元素是某个display为none的元素的子元素,那么渲染结点就不会被创建。结点相关的子类与CSS的display属性一起决定了该创建什么样的渲染结点。
Attach是一个自顶向下的递归过程。一个父结点的渲染结点的创建总会早于其子结点的渲染结点的创建。
销毁渲染树
在DOM结点被移除时或者整个文档被销毁时(比如关闭页面),渲染结点会随之销毁。此时DOM结点的detach方法就会被调用,用来销毁渲染结点。
void detach()
Detach是一个自底向上的递归操作。子结点的渲染结点会先于其父节点销毁。
获取样式信息
在attach过程中,DOM会通过CSS获取元素样式信息。结果会存储在RenderStyle对象中。
RenderStyle.h
每个WebKit支持的CSS属性都可通过该对象查询。RenderStyles对象是引用计数的对象,如果DOM结点创建了一个渲染结点,那么它将调用渲染结点的setStyle方法将样式信息与之关联。
void setStyle(RenderStyle*)
渲染结点增加了一个对样式对象的引用,该引用将一直被维护除非结点获得一个新样式或者被销毁。
RenderStyle可通过RenderObject的style()方法获得。
RenderStyle* style() const
CSS盒模型
RenderObject中的一个主要子类为RenderBox,该子类表示遵循CSS盒模型的对象,这些对象会有自己的border(边框)、padding(内边距)、margin(外边距)、width(宽)和height(高)。不过现在一些没有遵循CSS盒模型的对象也继承于RenderBox(例如:SVG对象)。这个问题以后会在重构渲染树的过程中修复。
CSS 2.1规范的这幅图描述了CSS盒模型的各个部分。下面的方法可用来获取边框和内外边距。RenderStyle只有在获取原始样式信息时用到,因为RenderObject的计算结果会有很大差别(特别是表格元素,它可以将单元格的padding属性覆盖,表格之间的边框也可以重叠在一起)。
int marginTop() const; int marginBottom() const; int marginLeft() const; int marginRight() const; int paddingTop() const; int paddingBottom() const; int paddingLeft() const; int paddingRight() const; int borderTop() const; int borderBottom() const; int borderLeft() const; int borderRight() const;
width()和height()方法给出宽度和高度(包含边框的)。
int width() const; int height() const;
client box包含了边框和滚动条,padding也包括:
int clientLeft() const { return borderLeft(); } int clientTop() const { return borderTop(); } int clientWidth() const; int clientHeight() const;
content box用来描述不包含border和padding的CSS盒子:
IntRect contentBox() const; int contentWidth() const { return clientWidth() - paddingLeft() - paddingRight(); } int contentHeight() const { return clientHeight() - paddingTop() - paddingBottom(); }
当盒子出现滚动条时,它被放在border和padding之间。滚动条的大小被包含在client width和client height中。滚动条不算在content box内。滚动条的大小以及当前滚动的位置可通过RenderObject获取。这个问题后续在单独章节中再详细说明。
int scrollLeft() const; int scrollTop() const; int scrollWidth() const; int scrollHeight() const;
盒子还有x和y的位置属性。这些位置相对于元素的父元素,父元素来决定盒子该放置在什么地方。这个规则存在许多例外情况,这也是渲染树中最令人困惑的地方。
int xPos() const; int yPos() const;