webkit網頁佈局(2)

一、繼續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的網頁佈局渲染有一定的基礎性作用。

發佈了3 篇原創文章 · 獲贊 0 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章