文章引用地址:http://blog.chinaunix.net/uid-580722-id-2090489.html

在有了对CSS网页布局标准及相关概念的认识之后,我们可以更加深入的理解WebKit究竟是如何实现其网页布局,同时实现对CSS布局标准的支持。
毕竟标准归标准,要高效的实现这些标准,不同的实现肯定有其不同的实现方式,就像不同的Web服务器对HTTP协议标准的实现有所不同一样,当然不同的实现也会增加一些自身特有的属性。
下面我们从数据结构的角度来了解WebKit中为实现网页布局所设计的主要类结构及其主要方法。

一、Render树的构成

在我们编写网页及使用JS的时候,大概都知道DOM树及其主要构成,了解到DOM树的构建其实质是对一个html或xml文件的内容采取树结构的方式来组织及描述,不同的标签及其在文档中的位置决定了其在整颗DOM树的地位及属性,针对具体DOM树的构成及不同树节点的描述,可以参考有关DOM的相关标准等,以后有机会我们也会单独来了解。

也许对于Render树大家就不那么了解了,简单的说来,它是对DOM树更进一步的描述,其描述的内容主要与布局渲染等CSS相关属性如left、top、width、height、color、font等有关,因为不同的DOM树结点可能会有不同的布局渲染属性,甚至布局时会按照标准动态生成一些匿名节点,所以为了更加方便的描述布局及渲染,WebKit内核又生成一颗Render树来描述DOM树的布局渲染等特性,当然DOM树与Render树不是一一对应,但可以相互关联,下面分别描述其主要节点:

1、基类RenderObject
RenderObject作为所有Render树节点的基类,完全类似与DOM树中的Node基类,它是构成Render树的基础,作用非比寻常,其中包含了构成Render树所可能涉及到的一些基本属性及方法,内容相当多,其主要数据成员及方法分别如下:
RenderObject主要数据成员

图一
其中成员m_parent、m_previous、m_next为构建Render树设置好关联基础;
m_Node则为DOM树中对应的节点;
m_style成员则描述该节点对应的各种CSS基本属性数据,下面会单独介绍;
至于其他的诸如m_positioned、m_isText、m_inline、m_floating、m_replaced等则描述其特性,就像CSS标准对不同元素的属性分类定义一样,从字面上我们就可以从上一节WebKit网页布局实现之基本概念及标准篇中可以找到它们这么定义的踪影。

成员m_needsPositionedMovementLayout、m_normalChildNeedsLayout、m_posChildNeedsLayout、m_needsLayout等主要用来描述该RenderObject是否确实需要重新布局;
当一个新的RenderObject对象插入到Render树的时候,它会设置其m_needsLayout属性为true,同时会根据该RenderObject对象在祖先RenderObject看来是一个positioned(拥有positiong:absolute或fixed属性)状态的孩子,如是则将相应祖先RenderObject对象的属性m_posChildNeedsLayout设置为true;

如果是一个in-flow(positon:static或relative)状态的孩子,则将相应祖先RenderObject对象的属性m_normalChildNeedsLayout设置为true;

主要方法:
//与是否需要layout相关
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; }

//与基本属性相关
bool isFloating() const { return m_floating; }
bool isPositioned() const { return m_positioned; } // absolute or fixed positioning
bool isRelPositioned() const { return m_relPositioned; } // relative positioning
bool isText() const { return m_isText; }
bool isInline() const { return m_inline; } // inline object
bool isCompact() const { return style()->display() == COMPACT; } // compact object
bool isRunIn() const { return style()->display() == RUN_IN; } // run-in object
bool isDragging() const { return m_isDragging; }
bool isReplaced() const { return m_replaced; } // a "replaced" element (see CSS)

//与外部DOM关联相关
RenderView* view() const;
// don't even think about making this method virtual!
Node* element() const { return m_isAnonymous ? 0 : m_node; }
Document* document() const { return m_node->document(); }
void setNode(Node* node) { m_node = node; }
Node* node() const { return m_node; }

// RenderObject tree manipulation
//////////////////////////////////////////
virtual bool canHaveChildren() const;
virtual bool isChildAllowed(RenderObject*, RenderStyle*) const { return true; }
virtual void addChild(RenderObject* newChild, RenderObject* beforeChild = 0);
virtual void removeChild(RenderObject*);
virtual bool createsAnonymousWrapper() const { return false; }

// raw tree manipulation
virtual RenderObject* removeChildNode(RenderObject*, bool fullRemove = true);
virtual void appendChildNode(RenderObject*, bool fullAppend = true);
virtual void insertChildNode(RenderObject* child, RenderObject* before, bool fullInsert = true);
// Designed for speed. Don't waste time doing a bunch of work like layer updating and repainting when we know that our
// change in parentage is not going to affect anything.
virtual void moveChildNode(RenderObject*);

virtual void paint(PaintInfo&, int tx, int ty);

/*
* This function should cause the Element to calculate its
* width and height and the layout of its content
*
* when the Element calls setNeedsLayout(false), layout() is no
* longer called during relayouts, as long as there is no
* style sheet change. When that occurs, m_needsLayout will be
* set to true and the Element receives layout() calls
* again.
*/
virtual void layout() = 0;

其中很多方法如paint()、layout()等是虚拟的,不同的子类可以重载它;

其中方法container() 、containingBlock()、paint()、layout()很值得大家深入研究;

总的说来RenderObject基类定义一些通用属性、方法,以便维护、布局、渲染Render树。

2、子类RenderBox

RenderBox代表描述CSS标准中的Box Model,它继承自RenderObject;
RenderBox主要数据成员

图二
其主要重载了部分继承而来的方法。

3、子类RenderContainer
在新的 WebKit 中,不再存在。

4、子类RenderFlow
RenderFlow主要用来描述CSS标准中提到的能进行inline-flow、block-flow相关处理的Render树结点,它继承自RenderContainer;
RenderFlow主要数据成员

图三
其主要方法包括在flow的过程中创建、关联匿名对象等;

5、子类RenderBlock
RenderBlock代表CSS标准中的block-level元素,它继承自RenderFlow;
RenderBlock主要数据成员

图四
它维护了一组由它定位的positioned树节点,以及有关overflow方面的设置;
其主要重载了RenderObject继承下来的layout、paint等方法;

因为html中的body、div、p等标签对应RenderBlock类对象,其在Render树具有非常重要的地位,其layout、paint等方法的实现,往往是WebKit整个布局、渲染处理的发起中心,内容比较多并且复杂,以后有机会详解。
6、子类RenderInline
RenderInline代表inline-level元素,其继承自RenderFlow,主要重载了RenderObject关于inline-flow方面处理的方法,提供了splitFlow、splitInlines等处理自动换行的方法。

7、子类RenderText
RenderText代表对html中Text node对应的Render树节点,它直接继承自RenderObject;
RenderText主要数据成员

图五
它提供关于处理文字方面如显示文字、行高计算、整个Text node对应的宽度等;它没有重载layout方法,因为它自身的定位往往由RenderBlock、RenderInline父对象来处理;

8、子类RenderImage
RenderImage代表html中img标签对应的树节点,它继承自RenderBox;
RenderImage继承关系及主要数据成员

图六
其主要提供关于图片显示、大小设置等方面的处理,其中paintReplaced方法将其图片显示出来;

9、子类RenderView
RenderView对应整个html文档对象的树节点,可看成是Render树的根,它继承自RenderBlock;
RenderView主要数据成员

图七
其中m_frameview成员对应整个文档对应的FrameView,而m_widgets则包括了该文档可能包含的plugin插件等对应的Render树节点;

RenderView对象作为Render树的根,它往往随着Document对象的创建而创建,它的layout、paint方法的发起往往是整颗Render树布局、渲染处理的开始;其中也包含了对选择处理。
10、其他
整个Render树中涉及的树节点类型,还有很多如RenderButton、RenderTable、RenderMedia等;并且各个类的方法及数据成员非常多,这里只是初步列出主要的类及其主要方法,特别是可能涉及到布局、渲染方方面的方法,以便我们能从中大致WebKit布局、渲染所涉及的基本内容及方法。

二、CSS属性的描述

1、RenderStyle类
RenderObject对象的m_style成员为RenderStyle类对象,它往往用来描述一个RenderObject所可能涉及的CSS属性数据(如left、top、align、color、font等等),其数据成员往往对应于CSS中定义的所有属性项,内容非常的庞杂,简单的说来就是将CSS标准中的所有属性按照一定分类定义到一个数据结构中。

2、RenderStyle类主要方法
为了获取、设置CSS属性所对应的值,RenderStyle类提供了所有的获取、设置CSS属性的方法如:
void setDisplay(EDisplay v) { noninherited_flags._effectiveDisplay = v; }
void setOriginalDisplay(EDisplay v) { noninherited_flags._originalDisplay = v; }
void setPosition(EPosition v) { noninherited_flags._position = v; }
void setFloating(EFloat v) { noninherited_flags._floating = v; }

void setLeft(Length v) { SET_VAR(surround,offset.left,v) }
void setRight(Length v) { SET_VAR(surround,offset.right,v) }
void setTop(Length v) { SET_VAR(surround,offset.top,v) }
void setBottom(Length v){ SET_VAR(surround,offset.bottom,v) }

void setWidth(Length v) { SET_VAR(box,width,v) }
void setHeight(Length v) { SET_VAR(box,height,v) }
等等。。。。

三、RenderObject及子类对象的生成

1、CSSParser
CSSParser类顾名思义,主要用来解析文本中各种CSS属性,并且有效的组织在一个RenderStyle对象中。
其主要方法parseValue、applyProperty的部分代码示例如下:
bool CSSParser::parseValue(int propId, bool important)
{
.....................................................
case CSSPropertyFloat:
// left | right | none | inherit + center for buggy CSS
if (id == CSSValueLeft || id == CSSValueRight ||
id == CSSValueNone || id == CSSValueCenter)
valid_primitive = true;
break;

case CSSPropertyClear: // none | left | right | both | inherit
if (id == CSSValueNone || id == CSSValueLeft ||
id == CSSValueRight|| id == CSSValueBoth)
valid_primitive = true;
break;

case CSSPropertyWebkitBoxAlign:
if (id == CSSValueStretch || id == CSSValueStart || id == CSSValueEnd ||
id == CSSValueCenter || id == CSSValueBaseline)
valid_primitive = true;
break;
.....................................................
case CSSPropertyWebkitBoxPack:
if (id == CSSValueStart || id == CSSValueEnd ||
id == CSSValueCenter || id == CSSValueJustify)
valid_primitive = true;
break;
....................................................
}

void CSSStyleSelector::applyProperty(int id, CSSValue *value)
{
case CSSPropertyOpacity:
HANDLE_INHERIT_AND_INITIAL(opacity, Opacity)
if (!primitiveValue || primitiveValue->primitiveType() != CSSPrimitiveValue::CSS_NUMBER)
return; // Error case.
// Clamp opacity to the range 0-1
m_style->setOpacity(min(1.0f, max(0.0f, primitiveValue->getFloatValue())));
return;
case CSSPropertyWebkitBoxAlign:
{
HANDLE_INHERIT_AND_INITIAL(boxAlign, BoxAlign)
if (!primitiveValue)
return;
EBoxAlignment boxAlignment = *primitiveValue;
if (boxAlignment != BJUSTIFY)
m_style->setBoxAlign(boxAlignment);
return;
}
...................................................
}

2、CSSStyleSelector类
CSSStyleSelector类其作用是基于所有用户的stylesheets集合为一个给定的DOM Element创建出其对应的RenderStyle对象。其主要功能由方法RenderStyle* styleForElement(Element*, RenderStyle* parentstyle=0, bool allowSharing = true, bool resolveForRootDefault = false);来实现。

3、构建Render树
在构建DOM树的过程中,Dom Element对象创建完后,往往通过attach方法来创建RenderObject对象,进而构建Render树。
其基本实现流程如下:void Element::attach()=>createRendererIfNeeded()=>createRenderer;

RenderObject* Element::createRenderer(RenderArena* arena, RenderStyle* style)
{
if (document()->documentElement() == this && style->display() == NONE) {
// Ignore display: none on root elements. Force a display of block in that case.
RenderBlock* result = new (arena) RenderBlock(this);
if (result)
result->setAnimatableStyle(style);
return result;
}
return RenderObject::createObject(this, style);
}

RenderObject* RenderObject::createObject(Node* node, RenderStyle* style)
{
Document* doc = node->document();
RenderArena* arena = doc->renderArena();

const ContentData* contentData = style->contentData();
if (contentData && !contentData->m_next && contentData->m_type == CONTENT_OBJECT && doc != node) {
RenderImageGeneratedContent* image = new (arena) RenderImageGeneratedContent(node);
image->setStyle(style);
if (StyleImage* styleImage = contentData->m_content.m_image)
image->setStyleImage(styleImage);
return image;
}

RenderObject* o = 0;

switch (style->display()) {//往往在CSSStyleSelector::styleForElement或CSSStyleSelector::adjustRenderStyle时//调用setDisplay()以确定其display属性。
case NONE:
break;
case INLINE:
o = new (arena) RenderInline(node);
break;
case BLOCK:
o = new (arena) RenderBlock(node);
break;
case INLINE_BLOCK:
o = new (arena) RenderBlock(node);
break;
case LIST_ITEM:
o = new (arena) RenderListItem(node);
break;
case RUN_IN:
case COMPACT:
o = new (arena) RenderBlock(node);
break;
case TABLE:
case INLINE_TABLE:
o = new (arena) RenderTable(node);
break;
case TABLE_ROW_GROUP:
case TABLE_HEADER_GROUP:
case TABLE_FOOTER_GROUP:
o = new (arena) RenderTableSection(node);
break;
case TABLE_ROW:
o = new (arena) RenderTableRow(node);
break;
case TABLE_COLUMN_GROUP:
case TABLE_COLUMN:
o = new (arena) RenderTableCol(node);
break;
case TABLE_CELL:
o = new (arena) RenderTableCell(node);
break;
case TABLE_CAPTION:
o = new (arena) RenderBlock(node);
break;
case BOX:
case INLINE_BOX:
o = new (arena) RenderFlexibleBox(node);
break;
}
return o;
}
这样就不同的DOM树节点结合不同的显示属性,创建出不同的RenderObject子类对象,进而形成一个Render树。

四、继续Render树的构成

1、子类RenderButton
RenderButton代表html中input标签type为button时对应的Render树节点,它直接继承自RenderFlexibleBox;
RenderFlexibleBox代表能按居中、左对齐、右对齐等水平或垂直方向布局子节点的树节点;
RenderButton主要数据成员

图八

其中m_buttonText为button上的文字对应的树节点,而m_inner为添加m_buttonText时创建的匿名对象,以便于居中等处理等。这些成员的创建来自于方法updateFromElement;
void RenderButton::updateFromElement()
{
// If we're an input element, we may need to change our button text.
if (element()->hasTagName(inputTag)) {
HTMLInputElement* input = static_cast(element());
String value = input->valueWithDefault();
setText(value);
}
}
void RenderButton::setText(const String& str)
{
..........................
m_buttonText = new (renderArena()) RenderTextFragment(document(), str.impl());
m_buttonText->setStyle(style());
addChild(m_buttonText);
......................
}
void RenderButton::addChild(RenderObject* newChild, RenderObject* beforeChild)
{
if (!m_inner) {
// Create an anonymous block.
m_inner = createAnonymousBlock();
m_inner->style()->setBoxFlex(1.0f);
RenderFlexibleBox::addChild(m_inner);
}
m_inner->addChild(newChild, beforeChild);
}
在缺省的html.css中对应button的css属性如下:
input[type="button"] {
-webkit-appearance: push-button;
white-space: pre
}
input[type="button"]{
-webkit-box-align: center;
text-align: center;
cursor: default;
color: ButtonText;
padding: 2px 6px 3px 6px;
border: 2px outset ButtonFace;
background-color: ButtonFace;
-webkit-box-sizing: border-box
}
这 些css属性通过CSSStyleSelector::applyProperty方法来设定其成员m_RenderStyle对应的值,其中包含 m_style->setAppearance(PushButtonAppearance);尤其值得关注,其初步决定了button是如何画出 来的。。

2、子类RenderTextControl
RenderTextControl代表html中input标签type为text或textarea标签对应的Render树节点,它直接继承自RenderBlock;
RenderTextControl主要数据成员

图九
其中成员m_multiLine以描述是textarea或text input;m_innerText为其中包括的文字对应的树节点;当作搜索按钮时m_cancelButton/m_resultsButton为对应的树节点;这些成员的创建来自于方法updateFromElement;
void RenderTextControl::updateFromElement()
{
HTMLFormControlElement* element = static_cast(node());

createSubtreeIfNeeded();

...................................

m_innerText->renderer()->style()->setUserModify(element->isReadOnlyControl() || element->disabled() ? READ_ONLY : READ_WRITE_PLAINTEXT_ONLY);

if ((!element->valueMatchesRenderer() || m_multiLine) && !m_placeholderVisible) {
String value;
if (m_multiLine)
value = static_cast(element)->value();
else
value = static_cast(element)->value();
if (value.isNull())
value = "";
else
value = value.replace('\\', backslashAsCurrencySymbol());
if (value != text() || !m_innerText->hasChildNodes()) {
if (value != text()) {
if (Frame* frame = document()->frame())
frame->editor()->clearUndoRedoOperations();
}
ExceptionCode ec = 0;
m_innerText->setInnerText(value, ec);
if (value.endsWith("\n") || value.endsWith("\r"))
m_innerText->appendChild(new HTMLBRElement(document()), ec);
m_dirty = false;
m_userEdited = false;
}
....................................
}
....................................
}
void RenderTextControl::createSubtreeIfNeeded()
{
............................................
if (!m_innerText) {
m_innerText = new HTMLTextFieldInnerTextElement(document(), m_innerBlock ? 0 : node());
RenderTextControlInnerBlock* textBlockRenderer = new (renderArena()) RenderTextControlInnerBlock(m_innerText.get());
m_innerText->setRenderer(textBlockRenderer);
m_innerText->setAttached();
m_innerText->setInDocument(true);

RenderStyle* parentstyle=style();
if (m_innerBlock)
parentstyle=m_innerBlock->renderer()->style();
RenderStyle* textBlockstyle=createInnerTextStyle(parentStyle);
textBlockRenderer->setStyle(textBlockStyle);

// Add text block renderer to Render tree
if (m_innerBlock) {
m_innerBlock->renderer()->addChild(textBlockRenderer);
ExceptionCode ec = 0;
// Add text block to the DOM
m_innerBlock->appendChild(m_innerText, ec);
} else
RenderBlock::addChild(textBlockRenderer);
}
.................................
}
在缺省的html.css中对应标签的css属性如下:
textarea {
-webkit-appearance: textarea;
background-color: white;
border: 1px solid;
-webkit-rtl-ordering: logical;
-webkit-user-select: text;
-webkit-box-orient: vertical;
resize: auto;
cursor: auto;
}
input, input[type="password"], input[type="search"], isindex {
-webkit-appearance: textfield;
padding: 1px;
background-color: white;
border: 2px inset;
-webkit-rtl-ordering: logical;
-webkit-user-select: text;
cursor: auto;
}
其中-webkit-appearance属性分别为textarea、textfield;

3、子类RenderListBox
RenderListBox代表html中select标签对应的Render树节点,它直接继承自RenderBlock;
RenderListBox主要数据成员

图十
其相关成员同样通过方法updateFromElement来设置初值;
在缺省的html.css中对应标签的css属性如下:
select {
-webkit-appearance: menulist;
-webkit-box-sizing: border-box;
-webkit-box-align: center;
border: 1px solid;
..............................................................
}

4、子类RenderTheme
RenderTheme在html标签中没有对应的页面元素,其作用主要用于如何渲染按钮、输入框、列表框等,其实现往往有一定平台相关性。
RenderTheme主要数据成员及方法

图十一
RenderTheme 往往提供一个接口,不同的图形库对其中不同的方法如paintbutton、paintcheckbox、painttextfield等进行了实现;其中 paint方法则根据appearance属性的不同以分别调用不同的paintxxx方法,其示例代码如下:
bool RenderTheme::paint(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
{
........................................
if (paintInfo.context->paintingDisabled())
return false;
// Call the appropriate paint method based off the appearance value.
switch (o->style()->appearance()) {
case CheckboxAppearance:
return paintCheckbox(o, paintInfo, r);
case RadioAppearance:
return paintRadio(o, paintInfo, r);
case PushButtonAppearance:
case SquareButtonAppearance:
case DefaultButtonAppearance:
case ButtonAppearance:
return paintButton(o, paintInfo, r);
case MenulistAppearance:
return paintMenuList(o, paintInfo, r);
break;
....................................................
default:
break;
}
return true; // We don't support the appearance, so let the normal background/border paint.
}
其中的appearance就是上面RenderButton、RenderTextControl、RenderListBox中提到的属性,至于 html中涉及到的类似标签或属性如radio、checkbox等等,其相关代码基本类似,至于不同的平台如Qt、Gtk、Win、Mac等究竟是如何画按钮、下拉框、列表框、多选框、单选框等等,则需详细参考RenderThemeQt/RenderThemeGtk/RenderThemeWin /RenderThemeMac等中的实现。

通过上述的了解我们应该对html中form标签内的输入框、按钮、下拉框等实现有了一定的认识。

5、子类RenderTable、RenderTableRow、RenderTableCol、RenderTableCell
这一组子类主要对应与html中table标签相关的树节点;
Table标签相关类主要数据成员

图十二
RenderTable主要通过addChild方法来维护对RenderTableCell、RenderTableCol、RenderTableRow等对象的管理及维护;
void RenderTableCell::updateFromElement()
{
Node* node = element();
if (node && (node->hasTagName(tdTag) || node->hasTagName(thTag))) {
HTMLTableCellElement* tc = static_cast(node);
int oldRSpan = m_rowSpan;
int oldCSpan = m_columnSpan;

m_columnSpan = tc->colSpan();
m_rowSpan = tc->rowSpan();
if ((oldRSpan != m_rowSpan || oldCSpan != m_columnSpan) && style() && parent()) {
setNeedsLayoutAndPrefWidthsRecalc();
if (section())
section()->setNeedsCellRecalc();
}
}
}

void RenderTableCol::updateFromElement()
{
int oldSpan = m_span;
Node* node = element();
if (node && (node->hasTagName(colTag) || node->hasTagName(colgroupTag))) {
HTMLTableColElement* tc = static_cast(node);
m_span = tc->span();
} else
m_span = !(style() && style()->display() == TABLE_COLUMN_GROUP);
if (m_span != oldSpan && style() && parent())
setNeedsLayoutAndPrefWidthsRecalc();
}
RenderTableRow通过方法layout和paint方法来布局管理RenderTableCell对象;
这一组子类主要实现人们熟知的表格布局,具体的实现可具体参考相关类实现;

6、子类RenderFrame
RenderFrame代表html中标签frame对应的Render树节点,其继承关系如下:
RenderFrame类继承关系

图十三

其中属性m_widget、m_view代表frame对应的widget及frameview,通过其中setwidget方法来设置m_widget属性,m_view属性则在对象创建的时候设置为当前document对应的frameview。

其中html中的embed/object插件标签对应的Render树节点为RenderPartObject对象。
7、构建Render树
从上一篇中我们了解到构建Render树的基本实现流程如下:void Element::attach()=>createRendererIfNeeded()=>createRenderer;以前我们着重了解过createRenderer,现在我们回头再来看看createRendererIfNeeded(),以更深入的了解是如何构建Render树。
void Node::createRendererIfNeeded()
{
if (!document()->shouldCreateRenderers())
return;
Node *parent = parentNode();
RenderObject *parentRenderer = parent->renderer();
if (parentRenderer && parentRenderer->canHaveChildren()
#if ENABLE(SVG)
&& parent->childShouldCreateRenderer(this)
#endif
) {
RenderStyle* style=styleForRenderer(parentRenderer);
if (rendererIsNeeded(style)) {
if (RenderObject* r = createRenderer(document()->renderArena(), style)) {
if (!parentRenderer->isChildAllowed(r, style))
r->destroy();
else {
setRenderer(r);
renderer()->setAnimatableStyle(style);
parentRenderer->addChild(renderer(), nextRenderer());
}
}
}
style->deref(document()->renderArena());
}
}

RenderStyle* Element::styleForRenderer(RenderObject* parentRenderer)
{
return document()->styleSelector()->styleForElement(this);
}

void RenderObject::setAnimatableStyle(RenderStyle* style)
{
if (!isText() && m_style && style)
style=animation()->updateImplicitAnimations(this, style);

setStyle(style);
}

从createRendererIfNeeded中我们可以了解到创建完RenderObject子类对象后,会为其设置RenderStyle属性,然后在该DOM Node的父节点对应的RenderObject中添加刚新建的RenderObject,这样以构建Render树。

但是在setStyle的过程中可能会调用createAnonymousFlow或createAnonymousBlock来创建匿名对象,其中RenderBox的setStyle方法如下:
void RenderBox::setStyle(RenderStyle* newStyle)
{
bool wasFloating = isFloating();
bool hadOverflowClip = hasOverflowClip();
RenderStyle* oldstyle=style();
if (oldStyle)
oldStyle->ref();

RenderObject::setStyle(newStyle);
....................................................................
setInline(newStyle->isDisplayInlineType());

switch (newStyle->position()) {
case AbsolutePosition:
case FixedPosition:
setPositioned(true);
break;
default:
setPositioned(false);

if (newStyle->isFloating())
setFloating(true);

if (newStyle->position() == RelativePosition)
setRelPositioned(true);
}

// We also handle and , whose overflow applies to the viewport.
if (!isRoot() && (!isBody() || !document()->isHTMLDocument()) && (isRenderBlock() || isTableRow() || isTableSection())) {
// Check for overflow clip.
if (newStyle->overflowX() != OVISIBLE) {
if (!hadOverflowClip)
// Erase the overflow
repaint();
setHasOverflowClip();
}
}
..............................................
if (requiresLayer()) {
if (!m_layer) {
if (wasFloating && isFloating())
setChildNeedsLayout(true);
m_layer = new (renderArena()) RenderLayer(this);
setHasLayer(true);
m_layer->insertOnlyThisLayer();
if (parent() && !needsLayout() && containingBlock())
m_layer->updateLayerPositions();
}
} else if (m_layer && !isRoot() && !isRenderView()) {
.......................................................
}
..................................................................
}

bool RenderObject::requiresLayer()
{
return isRoot() || isPositioned() || isRelPositioned() || isTransparent() || hasOverflowClip() || hasTransform() || hasMask() || hasReflection();
}

bool RenderObject::isRoot() const { return document()->documentElement() == node(); }
bool RenderObject::isPositioned() const { return m_positioned; } // absolute or fixed positioning
bool RenderObject::isRelPositioned() const { return m_relPositioned; } // relative positioning
bool RenderObject::isTransparent() const { return style()->opacity() <>RenderObject::hasOverflowClip() const { return m_hasOverflowClip; }
bool RenderObject::hasTransform() const { return m_hasTransform; }
bool RenderObject::hasMask() const { return style() && style()->hasMask(); }
bool RenderObject::hasReflection() const { return m_hasReflection; }

通过上面的了解我们知道在setStyle时符合一定条件的RenderObject会创建RenderLayer对象,那么究竟什么是RenderLayer类,其有什么作用,下面作初步的介绍。

五、RenderLayer树

1、类RenderLayer
RenderLayer类其实是一个非常复杂并且很重要的类,其主要数据成员如下:
RenderLayer类主要数据成员

图十四
RenderLayer类主要与处理分层布局、渲染页面元素等相关如处理z-index、opacity等,只有符合一个条件的RenderObject才会创建RenderLayer对象,并且将这些RenderLayer对象组织成一颗树。

2、构建RenderLayer树
通过方法insertOnlyThisLayer来组织这颗RenderLayer树。
void RenderLayer::insertOnlyThisLayer()
{
if (!m_parent && renderer()->parent()) {
// We need to connect ourselves when our renderer() has a parent.
// Find our enclosingLayer and add ourselves.
RenderLayer* parentLayer = renderer()->parent()->enclosingLayer();
RenderLayer* beforeChild = parentLayer->reflectionLayer() != this ? renderer()->parent()->findNextLayer(parentLayer, renderer()) : 0;
if (parentLayer)
parentLayer->addChild(this, beforeChild);
}

// Remove all descendant layers from the hierarchy and add them to the new position.
for (RenderObject* curr = renderer()->firstChild(); curr; curr = curr->nextSibling())
curr->moveLayers(m_parent, this);

// Clear out all the clip rects.
clearClipRects();
}

void RenderLayer::addChild(RenderLayer* child, RenderLayer* beforeChild)
{
RenderLayer* prevSibling = beforeChild ? beforeChild->previousSibling() : lastChild();
if (prevSibling) {
child->setPreviousSibling(prevSibling); prevSibling->setNextSibling(child);
} else
setFirstChild(child);

if (beforeChild) { beforeChild->setPreviousSibling(child); child->setNextSibling(beforeChild); } elsesetLastChild(child); child->setParent(this);

if (child->isOverflowOnly())
dirtyOverflowList();

if (!child->isOverflowOnly() || child->firstChild()) {
// Dirty the z-order list in which we are contained. The stackingContext() can be null in the
// case where we're building up generated content layers. This is ok, since the lists will start
// off dirty in that case anyway.
RenderLayer* stackingContext = child->stackingContext();
if (stackingContext)
stackingContext->dirtyZOrderLists();
}

child->updateVisibilityStatus();
if (child->m_hasVisibleContent || child->m_hasVisibleDescendant)
childVisibilityChanged(true);
}

static void addLayers(RenderObject* obj, RenderLayer* parentLayer, RenderObject*& newObject,
RenderLayer*& beforeChild)
{
if (obj->hasLayer()) {
if (!beforeChild && newObject) {
// We need to figure out the layer that follows newObject. We only do
// this the first time we find a child layer, and then we update the
// pointer values for newObject and beforeChild used by everyone else.
beforeChild = newObject->parent()->findNextLayer(parentLayer, newObject);
newObject = 0;
}
parentLayer->addChild(obj->layer(), beforeChild);
return;
}

for (RenderObject* curr = obj->firstChild(); curr; curr = curr->nextSibling())
addLayers(curr, parentLayer, newObject, beforeChild);
}

void RenderObject::addLayers(RenderLayer* parentLayer, RenderObject* newObject)
{
if (!parentLayer)
return;

RenderObject* object = newObject;
RenderLayer* beforeChild = 0;
WebCore::addLayers(this, parentLayer, object, beforeChild);
}

void RenderObject::removeLayers(RenderLayer* parentLayer)
{
if (!parentLayer)
return;

if (hasLayer()) {
parentLayer->removeChild(layer());
return;
}

for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling())
curr->removeLayers(parentLayer);
}

void RenderObject::moveLayers(RenderLayer* oldParent, RenderLayer* newParent)
{
if (!newParent)
return;

if (hasLayer()) {
if (oldParent)
oldParent->removeChild(layer());
newParent->addChild(layer());
return;
}

for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling())
curr->moveLayers(oldParent, newParent);
}

通过上面一组方法我们可以了解到拥有RenderLayer对象的RenderObject对象,按照Render树中最近的原则将含有的RenderLayer对象依Render树对应的父子关系组织RenderLayer树,RenderLayer对象的存在是依附于RenderObject对象而存在。

RenderView对象拥有对应的RenderLayer对象,同时其作为RenderLayer树根。

3、RenderLayer树与Render树的关系
通过RenderContainer::addChild方法回过头再来具体看看Render树自身的构成。
void RenderContainer::addChild(RenderObject* newChild, RenderObject* beforeChild)
{
bool needsTable = false;
//检查是否为Table的情况
if (needsTable) {
......................................................
} else {
// just add it...
insertChildNode(newChild, beforeChild);
}
.........................................................
}

void RenderContainer::insertChildNode(RenderObject* child, RenderObject* beforeChild, bool fullInsert)
{
if (!beforeChild) {
appendChildNode(child);
return;
}

while (beforeChild->parent() != this && beforeChild->parent()->isAnonymousBlock())
beforeChild = beforeChild->parent();

if (beforeChild == m_firstChild)
m_firstChild = child;

RenderObject* prev = beforeChild->previousSibling();
child->setNextSibling(beforeChild);
beforeChild->setPreviousSibling(child);
if(prev) prev->setNextSibling(child);
child->setPreviousSibling(prev);

child->setParent(this);

if (fullInsert) {
// Keep our layer hierarchy updated. Optimize for the common case where we don't have any children
// and don't have a layer attached to ourselves.
RenderLayer* layer = 0;
if (child->firstChild() || child->hasLayer()) {
layer = enclosingLayer(); child->addLayers(layer, child);
}

// if the new child is visible but this object was not, tell the layer it has some visible content
// that needs to be drawn and layer visibility optimization can't be used
if (style()->visibility() != VISIBLE && child->style()->visibility() == VISIBLE && !child->hasLayer()) {
if (!layer)
layer = enclosingLayer();
if (layer)
layer->setHasVisibleContent(true);
}
...........................................................
}

child->setNeedsLayoutAndPrefWidthsRecalc();
if (!normalChildNeedsLayout())
setChildNeedsLayout(true); // We may supply the static position for an absolute positioned child.
.................................................................
}
通过上面代码的了解我们知道通过addChild不仅维护Render树的构成,同时会将拥有的RenderLayer树构建起来。

4、RenderLayer树的作用
RenderLayer树的构建为渲染阶段处理z-index、opacity、overflow、scrollbar等打下一定的基础,在我们了解渲染的处理过程时我们再来深入的了解。

在这里我们初步的了解到在构建Render树的同时会维护一颗RenderLayer树,为分层布局、渲染作准备。

六、总结

其实WebKit涉及网页布局方面的数据结构还有关于SVG方面的,但通过上面的理解,如果对SVG感兴趣的话,应该对理解SVG有一定的参考作用。 当然数据结构方面还有相当多的内容未提及,这里只是列出一些关键类或结构,以便有个整体的抽象认识,希望能对了解WebKit的网页布局渲染有一定的基础性作用。