WebKit Layout 数据结构
生活随笔
收集整理的這篇文章主要介紹了
WebKit Layout 数据结构
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一、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樹。
四、總結
其實WebKit涉及網頁布局方面的數據結構遠不止這些,其中有的也比較復雜,這里只是列出自己認為較為重要的一小部分,希望能對了解WebKit的網頁布局渲染有一定的基礎性作用
總結
以上是生活随笔為你收集整理的WebKit Layout 数据结构的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android Canvas 图形绘制
- 下一篇: Layout坐标系 左上为0,0点,右/