WebCore Rendering 3 - 排版基础

来源:互联网 发布:远程控制软件排行 编辑:程序博客网 时间:2024/06/02 22:37

当渲染树节点首次创建并加入到渲染树时,它还没有任何位置和大小信息。计算各个盒(Box)的位置和大小的过程即为排版(layout)。每一个渲染树对象都有一个排版方法。

void layout()

排版是一个递归操作。一个名为FrameView的类,其代表了文档所在的容器视图,它也有一个排版方法。框架视图对象负责渲染树的排版。

FrameView可以执行两种类型的排版。一个是对整个渲染树进行排版(目前为止,最常用的排版类型)。这种类型的排版中,渲染树根节点对自己的排版方法进行调用,从而,整个渲染树的位置和大小都得到了更新。另一个只对渲染树的某个子树进行排版。当某个小的子树的排版不会影响到其周围的元素,就可以使用这种类型的排版。当前,子树排版仅应用于文本域(但是,在将来还可能应用于overflow:auto属性的块以及其他类似的结构)。

脏标志位(The Dirty Bits

排版过程通过使用一个脏标志位系统来决定一个对象是否需要排版。每当一个新的渲染树节点被加入到树中时,它们会设置自己以及祖先节点链中相关节点的脏标志位。渲染树使用了三个唯一的标志位。

bool needsLayout() const { return m_needsLayout || m_normalChildNeedsLayout || m_posChildNeedsLayout; }

bool selfNeedsLayout() const { return m_needsLayout; }

bool posChildNeedsLayout() const { return m_posChildNeedsLayout; }

bool normalChildNeedsLayout() const { return m_normalChildNeedsLayout; }

第一个标志位表示自己是否是脏的,可以通过方法selfNeedsLayout来查询。当这个标志位设置为真,其祖先节点会有一个标志位表示其有一个脏的孩子节点。而这个标志位是哪一种类型取决于链中前一个被设置为脏的节点的位置状态。posChildNeedsLayout用于表示一个positioned孩子被设置为脏。normalChildNeedsLayout用于表示一个in-flow孩子被设置为脏。区分这两种情况,可以对只存在postioned元素移动的情况下进行优化。

容器块(The Containing Block

“相关联的祖先节点链”是什么意思呢?当一个对象被标志为需要排版,祖先节点链被设置为脏是基于一个名叫容器块(containing block)的CSS概念。容器块也被用于建立其孩子的坐标空间。渲染树节点拥有xPosyPos坐标,这两个坐标是相对于它们的容器块的。那么,容器块到底是什么?

        这里的CSS 2.1规范文档有对这个概念的说明。

下面是通过WebCore渲染树相关的名词来介绍这个概念:

一个渲染树节点的容器块是一个负责决定这个渲染树节点位置的祖先块。

即,当排版递归过程从渲染树至上而下,是一个块负责将某些渲染树节点进行排版,而这个块就是这些渲染树节点的容器块。

渲染树的根节点是RenderView,而这个类对应于CSS 2.1里的初始容器块(inital containing block)。它也是Document对象的renderer()方法所返回的渲染树节点。

RenderView.h

初始容器块通常于可视区域大小一样。在桌面浏览器中,即为浏览器窗口的可见大小。而且,它会被设置为相对于整个文档的(0,0)的位置。下图是表示初始容器块在文档中的位置。黑色框表示的是RenderView,而灰色块则表示整个文档。

如果文档被滚动了,那么初始容器块会被移出窗口之外。初始容器块总是位于文档的最上方并且与可视区域大小一致。大家通常会对初始容器块有一个误解:初始容器块会在文档的外面,以及初始容器块是可视区域的一部分。

        这里的CSS 2.1规范文档有对容器块的详细说明。

一些规则总结如下:

l 根元素的渲染树节点(例如,<html>元素)将总是拥有RenderView作为其容器块。

l 如果一个渲染树节点的CSS position属性值为relativestatic,那么其容器块将为在渲染树中与其最近的块级祖先。

l 如果一个渲染树节点的CSS position属性值为fixed,那么其容器块为RenderView。从技术上来说,RenderView不是扮演着可视区域(viewport),所以,RenderJView必须对固定位置对象的坐标进行调整,以考虑文档滚动的情况。对于这种情况,“仅将RenderView扮演可视区域的容器块”,会比“为可视区域提供另一个渲染树节点”更简单。

l 如果一个渲染树节点的CSS position属性值为abssolute,那么其容器块是其最近的CSS position属性值不为static的块级祖先。如果没有这样的祖先存在,那么,其容器块即为RenderView

渲染树有两个的方法来方便地查询一个对象的positionabolute/fixed还是relative。这两个方法是:

bool isPositioned() const; // absolute or fixed positioning

bool isRelPositioned() const; // relative positioning

在绝大数代码当中,“positioned”表示的是CSS position属性值为absolutefixed的对象。“relPositioned”表示的是CSS position属性值为relative的对象。

渲染树有一个方法来获取渲染树节点的容器块。

RenderBlock* containingBlock() const

当一个对象被标志为需要排版时,它将沿着容器链对节点的nomalChildNeedsLayout标志位或posChildNeedsLayout标志位进行设置。容器链中的前一个节点的isPositioned状态决定哪一个标志位被设置。简单来说,容器链就是容器块链,但是对于容器链,中间的内联元素同样会被设置为脏。正因为这个不同,有另一个名为container的方法用来替换containingBlock方法,来决定用于脏设置的链。

RenderObject* container() const

layoutIfNeededsetNeedsLayout(false)

layoutIfNeeded方法(与AppKit中的术语displayIfNeeded方法相似),会对脏标志位被设置的渲染树节点进行排版操作。

void layoutIfNeeded()

全部排版方法通常以调用setNeedsLayout(flalse)结束。在结束排版方法之前清除脏标志十分重要,以免让之后的排版调用误以为对象仍是脏的。

一个排版方法的大致流程

从上层来看,一个排版方法通过会像类似于下面这样:

void layout()

{
ASSERT(needsLayout());

// Determine the width and horizontal margines of this object

...

for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {

// Determine if the chilld needs to get a relayout despite the dirty bit not being set.

...

// Place the child.

...

// Layout the child

Child->layoutIfNeeded();

...

}

// Now the intrinsic height of the object is known because the children are placed

// Determine the final height

...

setNeedsLayout(false);
}

我们将在以后的文章中深入讲述某些排版方法。

原文地址:http://www.webkit.org/blog/116/webcore-rendering-iii-layout-basics/ 


原创粉丝点击