DOM树
一个Web页将被解析成一棵树,树上的节点叫做Document Object
Model,简称DOM。树中所有节点的基类是Node。
节点可以分为几类,其中和rendering相关的类型是:
1. Document - 树根永远是document。有三种document类:Document,HTMLDocument和SVGDocument。第一种用于所有的XML文档,除去SVG文档;第二种用于HTML文档,继承自Document;第三种用于SVG文档,同样继承自Document。
2. Elments - HTML或XML资源中的所有tag都将对应到element。从rendering的角度,element是一个有tag的node,这个tag用于将节点转换成特定的子类,以便从中获取renderer所需的数据。
3. Text - element之间的纯文本将对应到text node。text节点存储纯文本,render tree可以从中查询字符数据。
Render树
rendering的核心是render树。render树和DOM树非常类似,其中的节点是对象object,这些对象可以对应到document,elements或者text节点。render树中也可以有额外的不对应到DOM节点的对象。
render树中所有节点的基类是RenderObject。
DOM节点的RenderObject可以通过Node的renderer()方法得到。
RenderObject* renderer() const
在遍历render树的时候,通常使用下面的方法:
RenderObject* firstChild() const;
RenderObject* lastChild() const;
RenderObject* previousSibling() const;
RenderObject* nextSibling() const;
这里是遍历一个renderer的直接孩子的循环:
for (RenderObject* child = firstChild(); child; child =
child->nextSibling()) {
...
}
创建Render树
renderer的创建是通过在DOM上执行attachment过程来完成的。在解析document的同时,添加DOM节点,在DOM节点上调用attach方法来创建renderer。
void attach()
attach方法为DOM节点计算style信息。如果element的CSS属性display被设置为none,或者该element是设置了display:none节点的后代,那么将不创建renderer。node的子类以及CSS显示属性值共同决定将为节点创建何种renderer。
attach是一个自顶向下的递归操作。父节点的renderer将先于其子孙节点的renderer进行创建。
销毁Render树
当DOM节点从document移除,或document被拆除(eg:由于其所在的tab/window被关闭)时,renderer将被销毁。调用DOM节点的detach方法来关闭连接,并销毁renderer。
void detach()
detach过程是自底向上递归进行的。后代节点的renderer将先于其父亲的renderer销毁。
Style信息的获得
在attach的过程中,DOM查询CSS以得element的style信息,这些信息将被保存在RenderStyle对象中。
WebKit支持的所有CSS属性都可以通过此对象查询。RenderStyle是引用计数的对象。当一个DOM节点创建一个renderer,将调用renderer的setStyle对象将style信息关联到renderer。
void setStyle(RenderStyle*)
renderer将增加它将维持的那个style的一个计数,直到其获得一个新的style或者自身被销毁。
可以通过RenderObject的style方法来获取其RenderStyle。
RenderStyle* style() const
CSS盒模型
RenderObject的子类中,最主要的就是RenderBox,其对应于那些遵循CSS盒模型的对象,包括那些有border,padding,margin,width和height的所有对象。目前,有一些不遵循CSS盒模型的对象(如SVG对象)也从RenderBox派生,这是一个未来将要修正的错误。(俺看了最近一个版本的webkit,仍然有一个叫做RenderSVGRoot的类派生自RenderBox,具体用途未知…)
链接中的CSS2.1规格说明书中的图标对CSS的box部分进行了说明。下述方法可以得到border、margin、padding的宽度。除非是要获得原始的纯style信息,否则不应该使用RenderStyle,因为实际上对RenderObject进行的计算是非常不同的(特别是对table,其可以覆盖单元的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;
下述方法给出box的宽和高,包含其border。
int width() const;
int height() const;
client box是去掉border和scrollbar,但保留padding的box区域。
int clientLeft() const { return borderLeft(); }
int clientTop() const { return borderTop(); }
int clientWidth() const;
int clientHeight() const;
context box指的是去除border和padding的CSS box区域。
IntRect contentBox() const;
int contentWidth() const { return clientWidth() - paddingLeft() -
paddingRight(); }
int contentHeight() const { return clientHeight() - paddingTop() -
paddingBottom(); }
当一个box有水平或者垂直滚动条,滚动条将被放在border和padding之间。滚动条的大小包含在client的宽和高之中,但并不属于content box。可滚动的区域和当前滚动位置都可以通过RenderObject得到。详细的内容将在专门章节中进行讲解。
int scrollLeft() const;
int scrollTop() const;
int scrollWidth() const;
int scrollHeight() const;
box同样具有x、y位置。这些位置和决定该box被放置于何处的祖先相关。对于此规则,有大量的例外存在,这也是render树最让人迷惑的地方。
int xPos() const;
int yPos() const;
源内容:
源内容:
This is the first of a series of posts designed to help people interested in hacking on WebCore’s rendering system. I’ll be posting these articles as I finish them on this blog, and they will also be available in the documentation section of the Web site.
The DOM Tree
A Web page is parsed into a tree of nodes called the Document Object Model (DOM for short). The base class for all nodes in the tree is
Node
.
Nodes break down into several categories. The node types that are relevant to the rendering code are:
- Document – The root of the tree is always the document. There are three document classes,
Document
,HTMLDocument
andSVGDocument
. The first is used for all XML documents other than SVG documents. The second applies only to HTML documents and inherits fromDocument
.
The third applies to SVG documents and also inherits fromDocument
. - Elements – All of the tags that occur in HTML or XML source turn into elements. From a rendering perspective, an element is a node with a tag name that can be used to cast to a specific subclass that can be queried for data that the renderer needs.
- Text – Raw text that occurs in between elements gets turned into text nodes. Text nodes store this raw text, and the render tree can query the node for its character data.
The Render Tree
At the heart of rendering is the render tree. The render tree is very similar to the DOM in that it is a tree of objects, where each object can correspond to the document, elements or text nodes. The render tree can also contain additional objects that have no corresponding DOM node.
The base class of all render tree nodes is
RenderObject
.
The
RenderObject
for a DOM node can be obtained using the renderer()
method on Node
.RenderObject* renderer() const
The following methods are most commonly used to walk the render tree.
RenderObject* firstChild() const;
RenderObject* lastChild() const;
RenderObject* previousSibling() const;
RenderObject* nextSibling() const;
Here is an example of a loop that walks a renderer’s immediate children. This is the most common walk that occurs in the render tree code.
for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
...
}
Creating the Render Tree
Renderers are created through a process on the DOM called attachment. As a document is parsed and DOM nodes are added, a method called attach gets called on the DOM nodes to create the renderers.
void attach()
The attach method computes style information for the DOM node. If the display CSS property for the element is set to none or if the node is a descendant of an element with display: none set, then no renderer will be created. The subclass of the node and the CSS display property value are used together to determine what kind of renderer to make for the node.
Attach is a top down recursive operation. A parent node will always have its renderer created before any of its descendants will have their renderers created.
Destroying the Render Tree
Renderers are destroyed when DOM nodes are removed from the document or when the document gets torn down (e.g., because the tab/window it was in got closed). A method called detach gets called on the DOM nodes to disconnect and destroy the renderers.
void detach()
Detachment is a bottom up recursive operation. Descendant nodes will always have their renderers destroyed before a parent destroys its renderer.
Accessing Style Information
During attachment the DOM queries CSS to obtain style information for an element. The resultant information is stored in an object called a RenderStyle.
Every single CSS property that WebKit supports can be queried via this object. RenderStyles are reference counted objects. If a DOM node creates a renderer, then it connects the style information to that renderer using the setStyle method on the renderer.
void setStyle(RenderStyle*)
The renderer adds a reference to the style that it will maintain until it either gets a new style or gets destroyed.
The
RenderStyle
can be accessed from a RenderObject
using the style()
method.RenderStyle* style() const
The CSS Box Model
One of the principal workhorse subclasses of
RenderObject
is RenderBox
. This subclass represents objects that obey the CSS box model. These include any objects that have borders, padding, margins, width and height. Right now some objects that do not follow the CSS box model (e.g., SVG objects) still subclass from RenderBox
. This is actually a mistake that will be fixed in the future through refactoring of the render tree.
This diagram from the CSS2.1 spec illustrates the parts of a CSS box. The following methods can be used to obtain the border/margin/padding widths. The
RenderStyle
should not be used unless the intent is to look at the original raw style information, since what is actually computed for theRenderObject
could be very different (especially for tables, which can override cell padding and have collapsed borders between cells).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;
The
width()
and height()
methods give the width and height of the box including its borders.int width() const;
int height() const;
The client box is the area of the box excluding borders and scrollbars. Padding is included.
int clientLeft() const { return borderLeft(); }
int clientTop() const { return borderTop(); }
int clientWidth() const;
int clientHeight() const;
The term content box is used to describe the area of the CSS box that excludes the borders and padding.
IntRect contentBox() const;
int contentWidth() const { return clientWidth() - paddingLeft() - paddingRight(); }
int contentHeight() const { return clientHeight() - paddingTop() - paddingBottom(); }
When a box has a horizontal or vertical scrollbar, it is placed in between the border and the padding. A scrollbar’s size is included in the client width and client height. Scrollbars are not part of the content box. The size of the scrollable area and the current scroll position can both be obtained from the
RenderObject
. I will cover this in more detail in a separate section on scrolling.int scrollLeft() const;
int scrollTop() const;
int scrollWidth() const;
int scrollHeight() const;
Boxes also have x and y positions. These positions are relative to the ancestor that is responsible for deciding where this box should be placed. There are numerous exceptions to this rule, however, and this is one of the most confusing areas of the render tree.
int xPos() const;
int yPos() const;
No comments:
Post a Comment