天天看點

WebKit網頁布局實作之主要架構篇

在有了對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_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

RenderContainer類用來描述可以擁有子RenderObject成員的容器類,它繼承自RenderBox;

RenderContainer主要資料成員

圖三

其主要重載了RenderObject提供的維護Render樹新增、删除樹節點等方面的方法。

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屬性的方法如:

等等。。。。

三、RenderObject及子類對象的生成

1、CSSParser

CSSParser類顧名思義,主要用來解析文本中各種CSS屬性,并且有效的組織在一個RenderStyle對象中。

其主要方法parseValue、applyProperty的部分代碼示例如下:

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樹。

其基本實作流程如下:

這樣就不同的DOM樹節點結合不同的顯示屬性,建立出不同的RenderObject子類對象,進而形成一個Render樹。

四、總結

其實WebKit涉及網頁布局方面的資料結構遠不止這些,其中有的也比較複雜,這裡隻是列出自己認為較為重要的一小部分,希望能對了解WebKit的網頁布局渲染有一定的基礎性作用。。

五、參考資源

<a href="http://webkit.org/">The WebKit Open Source Project</a>

繼續閱讀