1 Oct 2014

Webkitss css rendering appendix code and gpu process part 4

The Inline Block

One of the most confusingly named objects in CSS is the inline-block. Inline blocks are block flows that are designed to sit on a line. In effect they are like inline replaced elements on the outside, but on the inside they are block flows. The display property in CSS can be used to create inline blocks. Inline blocks will report true if asked if they are replaced.
div { display: inline-block }

Tables

Tables in HTML are block-level by default. However they can also be made into inlines using the CSS display property with a value of inline-table.
table { display: inline-table }
Again, from the outside an inline-table is like an inline replaced element (and will return true from isReplaced), but on the inside the object is still just a table.
In WebCore the RenderTable class represents a table. It inherits from RenderBlock for reasons that will be covered in the positioning section later.
RenderTable.h

Text

Raw text is represented using the RenderText class. Text is always considered inline by WebCore, since it is always placed on lines.
RenderText.h

Getting Block and Inline Information

The most basic method for obtaining block vs. inline status is the isInline function. This method asks if an object is designed to be part of a line. It does not care what the interior of the element is (e.g., text, image, an inline flow, an inline-block or an inline-table).
bool isInline() const
One of the common mistakes people make when working with the render tree is assuming that isInline means an object is always an inline flow, text or an inline replaced element. However because of inline-blocks and inline-tables, this method can return true even for these objects.
To ask if an object is actually a block or inline flow, the following methods should be used.
bool isInlineFlow() const
bool isBlockFlow() const
These methods are essentially asking questions about the interior of the object. An inline-block for example is still a block flow and not an inline flow. It is inline on the outside, but on the inside it is a block flow.
The exact class type can be queried for blocks and inlines using the following methods.
bool isRenderBlock() const  in RenderBlock.h
bool isRenderInline() const
The isRenderBlock method is useful in the context of positioning, since both block flows and tables act as positioned object containers.
To ask if an object is specifically an inline block or inline table, the isInlineBlockOrInlineTable method can be used.
bool isInlineBlockOrInlineTable() const

Children of Block Flows

Block flows have a simple invariant regarding their children that the render tree always obeys. That rule can be summarized as follows:
All in-flow children of a block flow must be blocks, or all in-flow children of a block flow must be inlines.
In other words, once you exclude floating and positioned elements, all of the children of a block flow in the render tree must return true from isInline or they must all return false from isInline. The render tree will change its structure as needed to preserve this invariant.
The childrenInline method is used to ask whether the children of a block flow are inlines or blocks.
bool childrenInline() const  (in RenderObject.h)

Children of Inline Flows

Children of inline flows have an even simpler invariant that must be maintained.
All in-flow children of an inline flow must be inlines.

Anonymous Blocks

In order to preserve the block flow child invariant (only inline children or only block children), the render tree will construct objects called anonymous blocks. Consider the following example:
<div>
Some text
<div>
Some more text
</div>
</div>
In the above example, the outer div has two children: some text and another div. The first child is an inline, but the second child is a block. Because this combination of children violates the all-inline or all-block child rule, the render tree will construct an anonymous block flow to wrap the text. The render tree therefore becomes:
<div>
<anonymous block>
Some text
</anonymous block>
<div>
Some more text
</div>
</div>
The isAnonymousBlock method can be used to ask if a renderer is an anonymous block flow.
bool isAnonymousBlock() const
Whenever a block flow has inline children and a block object suddenly tries to insert itself as a child, anonymous blocks will be created as needed to wrap all of the inlines. Contiguous inlines will share a single common anonymous block, so the number of anonymous blocks can be kept to a minimum. The makeChildrenNonInline method in RenderBlock is the function that performs this adjustment.
void makeChildrenNonInline(RenderObject *insertionPoint)

Blocks inside Inline Flows

One of the nastiest constructs you will see in HTML is when a block is placed inside an inline flow. Here is an example:
<i>Italic only <b>italic and bold
<div>
Wow, a block!
</div>
<div>
Wow, another block!
</div>
More italic and bold text</b> More italic text</i>
The two divs violate the invariant that all of the children of the bold element must be inlines. The render tree has to perform a rather complicated series of steps in order to fix up the tree. Three anonymous blocks are constructed. The first block holds all of the inlines that come before the divs. The second anonymous block holds the divs. The third anonymous block holds all of the inlines that come after the divs.
<anonymous pre block>
<i>Italic only <b>italic and bold</b></i>
</anonymous pre block>
<anonymous middle block>
<div>
Wow, a block!
</div>
<div>
Wow, another block!
</div>
</anonymous middle block>
<anonymous post block>
<i><b>More italic and bold text</b> More italic text</i>
</anonymous post block>
Notice that the bold and italic renderers had to split into two render objects, since they are in both the anonymous pre block and the anonymous post block. In the case of the bold DOM element, its children are in the pre block, but then continue into the middle block and then finally continue into the post block. The render tree connects these objects via a continuation chain.
RenderFlow* continuation() const
bool isInlineContinuation() const
The first bold renderer in the pre block is the one that can be obtained from the b DOM element using the element’s renderer() method. This renderer has as its continuation() the middle anonymous block. The middle anonymous block has as its continuation() the second bold renderer. In this way code that needs to examine the renderers that represent the children of the DOM element can still do so relatively easily.
In the above example the i DOM element also split. Its children are all in the pre and post blocks, and therefore only one connection is needed. The italic renderer in the pre block sets its continuation() to the italic renderer in the post block.
The function that performs the recursive splitting of inline flows and that creates the continuation chain connections is called splitFlow and is in RenderInline.cpp.

3 Responses to “WebCore Rendering II – Blocks and Inlines”

  1. Chris Griego Says:
    August 9th, 2007 at 4:26 pm
If the problem is having a block-level element within an inline element, then why is the anonymous middle block necessary?
  1. hyatt Says:
    August 9th, 2007 at 4:48 pm
Strictly speaking it isn’t necessary, although it simplifies a lot of the code that deals with continuations. It’s basically there for convenience.
  1. hyatt Says:
    August 9th, 2007 at 4:50 pm
Longer term, I’d like to explore eliminating continuations completely and wrapping the offending blocks in an anonymous inline-block instead. The difficult problem with using an anonymous inline-block, however, is that when the pre and post sections end up having no content, you have to margin collapse the offending blocks with surrounding content. This would necessitate having a sort of hybrid line/block layout that could easily slide into and out of both layout modes.

WebCore Rendering III – Layout Basics

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 called layout. All renderers have a layout method.
void layout()
Layout is a recursive operation. A class called FrameView represents the containing view for the document, and it also has a layout method. The frame view is responsible for managing the layout of the render tree.
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).

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.
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 method selfNeedsLayout. Whenever this bit is set to true, 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.

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 called the containing block. The containing block is also used to establish the coordinate space of children. Renderers have xPos and yPos coordinates, and these are relative to their containing blocks. So what exactly is a containing block?
Here is the CSS 2.1 spec’s introduction to the concept.

Containing blocks

In CSS 2.1, many box positions and sizes are calculated with respect to the edges of a rectangular box called a containing block. In general, generated boxes act as containing blocks for descendant boxes; we say that a box "establishes" the containing block for its descendants. The phrase "a box's containing block" means "the containing block in which the box lives," not the one it generates.
Each box is given a position with respect to its containing block, but it is not confined by this containing block; it may overflow.
The details of how a containing block's dimensions are calculated are described in the next chapter.

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

No comments:

Post a Comment