原帖地址 :http://blog.csdn.net/gmstart/article/details/6732334
一、继续Render树的构成
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的网页布局渲染有一定的基础 性作用。 |
|