WebKit Layout 数据结构
生活随笔
收集整理的這篇文章主要介紹了
WebKit Layout 数据结构
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
一、Render樹的構(gòu)成
在我們編寫網(wǎng)頁及使用JS的時(shí)候,大概都知道DOM樹及其主要構(gòu)成,了解到DOM樹的構(gòu)建其實(shí)質(zhì)是對一個(gè)html或xml文件的內(nèi)容采取樹結(jié)構(gòu)的方式來組織及描述,不同的標(biāo)簽及其在文檔中的位置決定了其在整顆DOM樹的地位及屬性,針對具體DOM樹的構(gòu)成及不同樹節(jié)點(diǎn)的描述,可以參考有關(guān)DOM的相關(guān)標(biāo)準(zhǔn)等,以后有機(jī)會(huì)我們也會(huì)單獨(dú)來了解。
也許對于Render樹大家就不那么了解了,簡單的說來,它是對DOM樹更進(jìn)一步的描述,其描述的內(nèi)容主要與布局渲染等CSS相關(guān)屬性如left、top、width、height、color、font等有關(guān),因?yàn)椴煌腄OM樹結(jié)點(diǎn)可能會(huì)有不同的布局渲染屬性,甚至布局時(shí)會(huì)按照標(biāo)準(zhǔn)動(dòng)態(tài)生成一些匿名節(jié)點(diǎn),所以為了更加方便的描述布局及渲染,WebKit內(nèi)核又生成一顆Render樹來描述DOM樹的布局渲染等特性,當(dāng)然DOM樹與Render樹不是一一對應(yīng),但可以相互關(guān)聯(lián),下面分別描述其主要節(jié)點(diǎn):
1、基類RenderObject
RenderObject作為所有Render樹節(jié)點(diǎn)的基類,完全類似與DOM樹中的Node基類,它是構(gòu)成Render樹的基礎(chǔ),作用非比尋常,其中包含了構(gòu)成Render樹所可能涉及到的一些基本屬性及方法,內(nèi)容相當(dāng)多,其主要數(shù)據(jù)成員及方法分別如下:
RenderObject主要數(shù)據(jù)成員 圖一
其中成員m_parent、m_previous、m_next為構(gòu)建Render樹設(shè)置好關(guān)聯(lián)基礎(chǔ); m_Node則為DOM樹中對應(yīng)的節(jié)點(diǎn);
m_style成員則描述該節(jié)點(diǎn)對應(yīng)的各種CSS基本屬性數(shù)據(jù),下面會(huì)單獨(dú)介紹;
至于其他的諸如m_positioned、m_isText、m_inline、m_floating、m_replaced等則描述其特性,就像CSS標(biāo)準(zhǔn)對不同元素的屬性分類定義一樣,從字面上我們就可以從上一節(jié)WebKit網(wǎng)頁布局實(shí)現(xiàn)之基本概念及標(biāo)準(zhǔn)篇中可以找到它們這么定義的蹤影。
成員m_needsPositionedMovementLayout、m_normalChildNeedsLayout、m_posChildNeedsLayout、m_needsLayout等主要用來描述該RenderObject是否確實(shí)需要重新布局;
當(dāng)一個(gè)新的RenderObject對象插入到Render樹的時(shí)候,它會(huì)設(shè)置其m_needsLayout屬性為true,同時(shí)會(huì)根據(jù)該RenderObject對象在祖先RenderObject看來是一個(gè)positioned(擁有positiong:absolute或fixed屬性)狀態(tài)的孩子,如是則將相應(yīng)祖先RenderObject對象的屬性m_posChildNeedsLayout設(shè)置為true;
如果是一個(gè)in-flow(positon:static或relative)狀態(tài)的孩子,則將相應(yīng)祖先RenderObject對象的屬性m_normalChildNeedsLayout設(shè)置為true;
主要方法:
//與是否需要layout相關(guān)
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; }
//與基本屬性相關(guān)
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關(guān)聯(lián)相關(guān)
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基類定義一些通用屬性、方法,以便維護(hù)、布局、渲染Render樹。
2、子類RenderBox
RenderBox代表描述CSS標(biāo)準(zhǔn)中的Box Model,它繼承自RenderObject; RenderBox主要數(shù)據(jù)成員 圖二
其主要重載了部分繼承而來的方法。
3、子類RenderContainer 在新的 WebKit 中,不再存在。
4、子類RenderFlow
RenderFlow主要用來描述CSS標(biāo)準(zhǔn)中提到的能進(jìn)行inline-flow、block-flow相關(guān)處理的Render樹結(jié)點(diǎn),它繼承自RenderContainer; RenderFlow主要數(shù)據(jù)成員 圖四
其主要方法包括在flow的過程中創(chuàng)建、關(guān)聯(lián)匿名對象等;
5、子類RenderBlock
RenderBlock代表CSS標(biāo)準(zhǔn)中的block-level元素,它繼承自RenderFlow;
RenderBlock主要數(shù)據(jù)成員 圖五
它維護(hù)了一組由它定位的positioned樹節(jié)點(diǎn),以及有關(guān)overflow方面的設(shè)置; 其主要重載了RenderObject繼承下來的layout、paint等方法;
因?yàn)閔tml中的body、div、p等標(biāo)簽對應(yīng)RenderBlock類對象,其在Render樹具有非常重要的地位,其layout、paint等方法的實(shí)現(xiàn),往往是WebKit整個(gè)布局、渲染處理的發(fā)起中心,內(nèi)容比較多并且復(fù)雜,以后有機(jī)會(huì)詳解。 6、子類RenderInline
RenderInline代表inline-level元素,其繼承自RenderFlow,主要重載了RenderObject關(guān)于inline-flow方面處理的方法,提供了splitFlow、splitInlines等處理自動(dòng)換行的方法。
7、子類RenderText
RenderText代表對html中Text node對應(yīng)的Render樹節(jié)點(diǎn),它直接繼承自RenderObject;
RenderText主要數(shù)據(jù)成員 圖六
它提供關(guān)于處理文字方面如顯示文字、行高計(jì)算、整個(gè)Text node對應(yīng)的寬度等;它沒有重載layout方法,因?yàn)樗陨淼亩ㄎ煌蒖enderBlock、RenderInline父對象來處理;
8、子類RenderImage RenderImage代表html中img標(biāo)簽對應(yīng)的樹節(jié)點(diǎn),它繼承自RenderBox; RenderImage繼承關(guān)系及主要數(shù)據(jù)成員 圖七
其主要提供關(guān)于圖片顯示、大小設(shè)置等方面的處理,其中paintReplaced方法將其圖片顯示出來;
9、子類RenderView RenderView對應(yīng)整個(gè)html文檔對象的樹節(jié)點(diǎn),可看成是Render樹的根,它繼承自RenderBlock; RenderView主要數(shù)據(jù)成員 圖八
其中m_frameview成員對應(yīng)整個(gè)文檔對應(yīng)的FrameView,而m_widgets則包括了該文檔可能包含的plugin插件等對應(yīng)的Render樹節(jié)點(diǎn);
RenderView對象作為Render樹的根,它往往隨著Document對象的創(chuàng)建而創(chuàng)建,它的layout、paint方法的發(fā)起往往是整顆Render樹布局、渲染處理的開始;其中也包含了對選擇處理。 10、其他
整個(gè)Render樹中涉及的樹節(jié)點(diǎn)類型,還有很多如RenderButton、RenderTable、RenderMedia等;并且各個(gè)類的方法及數(shù)據(jù)成員非常多,這里只是初步列出主要的類及其主要方法,特別是可能涉及到布局、渲染方方面的方法,以便我們能從中大致WebKit布局、渲染所涉及的基本內(nèi)容及方法。
二、CSS屬性的描述
1、RenderStyle類
RenderObject對象的m_style成員為RenderStyle類對象,它往往用來描述一個(gè)RenderObject所可能涉及的CSS屬性數(shù)據(jù)(如left、top、align、color、font等等),其數(shù)據(jù)成員往往對應(yīng)于CSS中定義的所有屬性項(xiàng),內(nèi)容非常的龐雜,簡單的說來就是將CSS標(biāo)準(zhǔn)中的所有屬性按照一定分類定義到一個(gè)數(shù)據(jù)結(jié)構(gòu)中。
2、RenderStyle類主要方法
為了獲取、設(shè)置CSS屬性所對應(yīng)的值,RenderStyle類提供了所有的獲取、設(shè)置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屬性,并且有效的組織在一個(gè)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集合為一個(gè)給定的DOM Element創(chuàng)建出其對應(yīng)的RenderStyle對象。其主要功能由方法RenderStyle* styleForElement(Element*, RenderStyle* parentstyle=0, bool allowSharing = true, bool resolveForRootDefault = false);來實(shí)現(xiàn)。
3、構(gòu)建Render樹
在構(gòu)建DOM樹的過程中,Dom Element對象創(chuàng)建完后,往往通過attach方法來創(chuàng)建RenderObject對象,進(jìn)而構(gòu)建Render樹。
其基本實(shí)現(xiàn)流程如下: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時(shí)//調(diào)用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樹節(jié)點(diǎn)結(jié)合不同的顯示屬性,創(chuàng)建出不同的RenderObject子類對象,進(jìn)而形成一個(gè)Render樹。
四、總結(jié)
其實(shí)WebKit涉及網(wǎng)頁布局方面的數(shù)據(jù)結(jié)構(gòu)遠(yuǎn)不止這些,其中有的也比較復(fù)雜,這里只是列出自己認(rèn)為較為重要的一小部分,希望能對了解WebKit的網(wǎng)頁布局渲染有一定的基礎(chǔ)性作用
總結(jié)
以上是生活随笔為你收集整理的WebKit Layout 数据结构的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android Canvas 图形绘制
- 下一篇: Layout坐标系 左上为0,0点,右/