WebCore Rendering III – Layout Basics

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

译文:

当renderer被首次创建并加入到树中时,他们还没有位置和大小等信息。这些信息的获得是在调用了layout方法之后才生成的,所有的renderer都有一个layout方法。

void layout()

layout是一个递归的操作,FrameView类展示了一个文档的containing view,他也具有一个layout方法,framview负责管理render tree的layout。

FrameView有两种类型的layout:第一种(最常见的一种)是完成整个render tree的layout,在这种情况下,render tree的根节点调用layout方法,导致整个render tree被更新。第二种类型的layout将更新限制在render tree中的某个特定子树,这被用来去更新一些小的子树,而不影响他周围的元素。截止到目前,子树layout仅用在text field。(但将来或许被应用在overflow:auto blocks和其他相似的构建)

The Dirty Bits

layout使用一个dirty bit system来决定是否一个对象需要layout。 当一个新的renderer被加入到render tree中时,他把自己和其祖先链(ancestor chain)中的相关链接都标注为dirty。render tree使用三个unique 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; }

如果renderer自身是dirty,那么第一个bit被设置, 可以通过方法selfNeedsLayout来进行查询,当这个bit被设置为true,相关祖先的renderer设置其相关的bit表示它含有一个dirty child。被设置的bit的类型取决于dirtied链的前面链接(previous link )的位置状态(这句有点困惑不知怎么翻译好,原文:The type of bit set depends on the positioned status of the previous link in the chain being dirtied. ) posChildNeedsLayout用来表示一个positioned孩子是dirty。 normalChildNeedsLayout用来表示一个in-low孩子是dirty。通过对这两种类型孩子的区分,layout能够对仅仅是positioned 元素改变的情况进行优化。// 译者:比较困惑什么是positioned和in-flow child,两者有什么不同?

The Containing Block

我所说的“相关的祖先链(the relevant ancestor chain)”到底是什么意思呢? 当一个对象被标识为需要layout, 他的dirty的祖先链(ancestor chain)是基于CSS中包含block(containing block)这个概念来定义的。包含block(containing block)也被用来建立孩子的坐标空间。 renderers具有xPos和yPos坐标,但这些坐标是相对于他们的包含block(containing block)的, 什么是containing block的精确定义呢?

在CSS2.1 spc中介绍了这个概念。

对于Containing block,我自己在WebCore中对这个概念的解读如下:

一个renderer containing block是这个renderer的祖先block,他决定了这个renderer的位置,

换句话说,在递归向下的layout一个render tree时,那个决定了所有renderers的位置的block就是他们的containing block。//译者:这个解释似乎更难理解,呵呵。。。

RenderView.h

初始的containing block总是设置为和viewport一样的size, 对于桌面浏览器来说,就是浏览器窗口的可见部分。containing block被放置在相对于整个文档的位置(0,0)点。下面这个图演示了,一个文档的初始containing block如何放置,其中黑色边框的box代表RenderView,灰色box表示这个文档。

 

如果一个文档被滚动了,那么初始containing block将移出屏幕,他总是在文档的最上面,和viewport具有同样size。对于初始containing block人们经常有的一个困惑就是:他们希望初始containing block在某种程度上outsite the document并且是part of the viewport。

这里是在CSS2.1中关于containing block specification的细节信息

这些规则可以总结如下:

  • 根节点元素的renderer(例如:<html>元素)将总是以RenderView作为他的containing block
  • 如果一个renderer具有一个CSS的relative或static position属性,那么他的containing block将是render tree中最近的block-level祖先
  • 如果renderer具有一个CSS的fixed position属性,那么他的containing block将是RenderView。从技术上讲,RenderView与viewport不同,因此RenderView不得不根据文档滚动的位置来调整fixed positioned对象的坐标。其实,在这种情况下,使RenderView表现的像一个viewport containing block比让viewport具有一个单独的renderer更加简单。
  • 如果一个renderer具有一个CSS的absolute position的属性,那么他的containing block将是最近的block-level的具有非static position属性的祖先,如果这样的祖先不存在,那么containing block将是RenderView

render tree提供了两个方便的方法来查询一个对象是否具有一个absolute、fixed或者relative position属性,他们是:

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

纵观大部分代码positioned这个词指的是CSS中的absolute和fixed position 对象,relPositioned指的是relative positioned的对象。

render tree中有一个方法来获得一个renderer的containing block,就是:

RenderBlock* containingBlock() const

当一个对象被标记为需要layout, 他遍历一个container chain设置normalChildNeedsLayout位或者posChildNeedslayout位, 这个chain中的previous linkd的isPositioned的状态决定了哪一个位将被设置。container chain大致对应于containing block chain,虽然中间的inline仍然是dirtied,正因为这个不同点,采用了一个不同的方法container()取代方法containingBlock()来判定这个chain for dirtying。

 

RenderObject* container() const

layoutIfNeeded and setNeedsLayout(false)

layoutIfNeeded方法(与AppKit的displayIfNeeded方法术语相似)是一个方便的方法来告诉renderer如果dirty bit被设置了,那么就进行layout。

void layoutIfNeeded()

一般的,layout方法会以setNeedsLayout(false)结束,在离开方法前清除dirty bits是非常重要的,这样可以使未来的layout调用不会误以为一个对象仍然dirty。

Anatomy of a Layout Method

Layout方法的剖析

 

一个高层的layout方法通常会像下面这样:

void layout()
{
    ASSERT(needsLayout());

    // Determine the width and horizontal margins of this object.
    ...

    for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
        // Determine if the child needs to get a relayout despite the dirty bit not being set.
        ...

        // Place the child.
        ...

        // Lay out the child
        child->layoutIfNeeded();

       ...
    }

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

    setNeedsLayout(false);
}

在未来的文章中,我们将深入特定的layout方法。

 

 

--------------------------------------------------英汉对照-----------------------------------------------------

Posted by Dave Hyatt on Friday, August 10th, 2007 at 2:01 pm

When renderers are first created and added to the tree, they have no position or size yet. The process by which all of the boxes have their positions and sizes determined is calledlayout. All renderers have alayout method.

当renderer被首次创建并加入到树中时,他们还没有位置和大小等信息。这些信息的获得是在调用了layout方法之后才生成的,所有的renderer都有一个layout方法。

void layout()

Layout is a recursive operation. A class called FrameView represents the containing view for the document, and it also has alayout method. The frame view is responsible for managing the layout of the render tree.

layout是一个递归的操作,FrameView类展示了一个文档的containing view,他也具有一个layout方法,framview负责管理render tree的layout。

There are two types of layout that the FrameView can perform. The first (and by far the most common) is a layout of the entire render tree. In this case the root of the render tree has its layout method called and then the entire render tree gets updated. The second type of layout is constrained to a specific subtree of the render tree. It is used in situations where the layout of some small subtree can’t possibly affect its surroundings. Right now the subtree layout is only used by text fields (but may be used by overflow:auto blocks and other similar constructs in the future).

FrameView有两种类型的layout:第一种(最常见的一种)是完成这个render tree的layout,在这种情况下,render tree的根节点调用layout方法,导致整个render tree被更新。第二中类型的layout将更新限制在render tree中的某个特定子树,这被用来去更新一些小的子树,而不影响他周围的元素。截止到目前,子树layout仅用在text field。(但将来或许被应用在overflow:auto blocks和其他相似的构建)

The Dirty Bits

Layout uses a dirty bit system to determine whether an object actually needs a layout. Whenever new renderers are inserted into the tree, they dirty themselves and the relevant links in their ancestor chain. There are three unique bits that are used by the render tree.

layout使用一个dirty bit system来决定是否一个对象需要一个layout。 当一个新的renderer被加入到render tree中时,他把自己和其祖先链中的相关链接都标注为dirty。render tree使用三个unique 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; }

The first bit is used when the renderer itself is dirty, and it can be queried using the methodselfNeedsLayout. Whenever this bit is set totrue, relevant ancestor renderers have bits set indicating that they have a dirty child. The type of bit set depends on the positioned status of the previous link in the chain being dirtied.posChildNeedsLayout is used to indicate that a positioned child got dirtied.normalChildNeedsLayout is used to indicate that an in-flow child got dirtied. By distinguishing between these two types of children, layout can be optimized for the case where only positioned elements moved.

第一个bit被设置,如果renderer自身是dirty, 可以通过方法selfNeedsLayout来进行查询,当这个bit被设置为true,相关祖先的renderer设置其相关的bit表示它含有一个dirty child。被设置的bit的类型取决于dirtied链的前面链接的位置状态(这句有点困惑不知怎么翻译好,原文:The type of bit set depends on the positioned status of the previous link in the chain being dirtied. ) posChildNeedsLayout用来表示一个positioned孩子是dirty。 normalChildNeedsLayout用来表示一个in-low孩子是dirty。通过对这两种类型孩子的区分,layout能够对仅仅是positioned 元素改变的情况进行优化。// 译者:比较困惑什么是positioned和in-flow child,两者有什么不同?

The Containing Block

What exactly did I mean by “the relevant ancestor chain”? When an object is marked as needing layout, the ancestor chain that is dirtied is based on a CSS concept calledthe containing block. Thecontaining block is also used to establish the coordinate space of children. Renderers havexPos andyPos coordinates, and these are relative to their containing blocks. So what exactly is a containing block?

我所说的“相关的祖先链(the relevant ancestor chain)”到底是什么意思呢? 当一个对象被标识为需要layout, 他的dirty的祖先链是基于CSS中包含block(containing block)这个概念来定义的。包含block(containing block)也被用来建立孩子的坐标空间。 renderers具有xPos和yPos坐标,这些坐标是相对于他们的包含block(containing block)的, 但什么是containing block的精确定义呢?

Here is the CSS 2.1 spec’s introduction to the concept.

在CSS2.1 spc中介绍了这个概念。

My own way of introducing the concept in terms of the WebCore render tree would be as follows:

A renderer’s containing block is an ancestor block of the renderer that is responsible for determining that renderer’s position.

In other words when layout recurs down the render tree, it is a block’s responsibility to position all of the renderers that have it as their containing block.

The root of the render tree is called the RenderView, and this class corresponds to theinitial containing block according to CSS2.1. It is also the renderer that will be returned if therenderer() method is called on the Document.

我自己在WebCore中对这个概念的解读如下:

一个renderer的包含block(containing block)是这个renderer的祖先block,他决定了这个renderer的位置,

换句话说,在递归向下的layout一个render tree时,那个决定了所有renderers的位置的block就是他们的containing block。//译者:这个解释似乎更难理解,呵呵。。。

RenderView.h

The initial containing block is always sized to the viewport. In desktop browsers, this is the visible area in the browser window. It is also always at position (0,0) relative to the entire document. Here’s a picture illustrating where the initial containing block is positioned for a document. The black bordered box represents theRenderView, and the grey box represents the entire document.

初始的containing block总是设置为和viewport一样的size, 对于桌面浏览器来说,就是浏览器窗口的可见部分。containing block被放置在相对于整个文档的位置(0,0)点。下面这个图演示了,一个文档的初始containing block放置在那里,其中黑色边框的box代表RenderView,灰色box表示这个文档。

 

If the document is scrolled, then the initial containing block will be moved off screen. It is always at the top of the document and sized to the viewport. One area of confusion that people often have with the initial containing block is that they expect it to somehow be outside the document and part of the viewport.

如果一个文档被滚动了,那么初始containing block将移出屏幕,他总是在文档的最上面,和viewport具有同样size。对于初始containing block人们经常有的一个困惑就是:他们希望初始containing block在某种程度上outsite the document并且是part of the viewport。

Here is the detailed containing block specification in CSS2.1.

这里是在CSS2.1中关于containing block specification的细节信息

The rules can be summarized as follows:

这些规则可以总结如下:

  • The root element’s renderer (i.e., the <html> element) will always have the RenderView as its containing block.
  • If a renderer has a CSS position of relative or static, then the containing block will be the nearest block-level ancestor in the render tree.
  • If a renderer has a CSS position of fixed, then the containing block will be the RenderView. Technically the RenderView does not act as a viewport, so the RenderView has to adjust the coordinates of fixed positioned objects to account for the document scroll position. It is simpler to just let the RenderView act like a viewport containing block for this one case rather than having a separate renderer just for the viewport.
  • If a renderer has a CSS position of absolute, then the containing block is the nearest block-level ancestor with a position other than static. If no such ancestor exists, then the containing block will be the RenderView.
  • 根节点元素的renderer(例如:<html>元素)将总是以RenderView作为他的containing block
  • 如果一个renderer具有一个CSS的relative或static position属性,那么他的containing block将是render tree中最近的block-level祖先
  • 如果renderer具有一个CSS的fixed position属性,那么他的containing block将是RenderView。从技术上讲,RenderView与viewport不同,因此RenderView不得不根据文档滚动的位置来调整fixed positioned对象的坐标。其实,在这种情况下,使RenderView表现的像一个viewport containing block比让viewport具有一个单独的renderer更加简单。
  • 如果一个renderer具有一个CSS的absolute position的属性,那么他的containing block将是最近的block-level的具有非static position属性的祖先,如果这样的祖先不存在,那么containing block将是RenderView

The render tree has two convenience methods for asking if an object has a position of absolute, fixed or relative. They are:

render tree提供了两个方便的方法来查询一个对象是否具有一个absolute、fixed或者relative position属性,他们是:

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

Throughout most of the code the term positioned refers to both absolute and fixed positioned objects in CSS. The termrelPositioned refers to relative positioned objects in CSS.

纵观大部分代码positioned这个词指的是CSS中的absolute和fixed position 对象,relPositioned指的是relative positioned的对象。

The render tree has a method for obtaining the containing block of a renderer.

render tree中有一个方法来获得一个renderer的containing block,就是:

RenderBlock* containingBlock() const

When an object is marked as needing layout, it walks up a container chain setting either thenormalChildNeedsLayout bit or theposChildNeedsLayout bit. TheisPositioned status of the previous link in the chain determines what bit gets set. The container chain roughly corresponds to the containing block chain, although intermediate inlines are still dirtied as well. A different method calledcontainer is used instead ofcontainingBlock to determine the chain for dirtying because of this distinction.

当一个对象被标记为需要layout, 他遍历一个container chain设置normalChildNeedsLayout位或者posChildNeedslayout位, 在这个chain中的previous linkd的isPositioned的状态决定了哪一个位将被设置。container chain大致对应于containing block chain,虽然中间的inline仍然是dirtied,正因为这个不同点,采用了一个不同的方法container()取代方法containingBlock()来判定这个chain for dirtying。

 

RenderObject* container() const

layoutIfNeeded and setNeedsLayout(false)

The layoutIfNeeded method (similar in terminology to AppKit’s displayIfNeeded method) is a convenient shorthand for telling a renderer to only do a layout if it has a dirty bit set.

layoutIfNeeded方法(与AppKit的displayIfNeeded方法术语相似)是一个方便的方法来告诉renderer如果dirty bit被设置了,那么就进行layout。

void layoutIfNeeded()

All layout methods typically end with setNeedsLayout(false). It is important to clear dirty bits on the renderer before leaving the layout method, so that future layout calls won’t mistakenly think that the object is still dirty.

一般的,layout方法会以setNeedsLayout(false)结束,在离开方法前清除dirty bits是非常重要的,这样可以使未来的layout调用不会误以为一个对象仍然dirty。

Anatomy of a Layout Method

Layout方法的剖析

 

At a high level layout methods usually look something like this:

一个高层的layout方法通常会像下面这样:

void layout()
{
    ASSERT(needsLayout());

    // Determine the width and horizontal margins of this object.
    ...

    for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
        // Determine if the child needs to get a relayout despite the dirty bit not being set.
        ...

        // Place the child.
        ...

        // Lay out the child
        child->layoutIfNeeded();

       ...
    }

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

    setNeedsLayout(false);
}

We will drill into specific layout methods in future articles.

在未来的文章中,我们将深入特定的layout方法。

 

你可能感兴趣的:(css,object,tree,layout,文档,methods)