配色: 字号:
Chromium网页Render Layer Tree创建过程分析
2016-10-25 | 阅:  转:  |  分享 
  
Chromium网页RenderLayerTree创建过程分析

在前面一文中,我们分析了网页RenderObjectTree的创建过程。在创建RenderObjectTree的同时,WebKit还会创建RenderLayerTree,但不是每一个RenderObject都有对应的RenderLayer。RenderLayer是一个最小渲染单元,被若干RenderObject共用。本文接下来就分析RenderLayerTree的创建过程。



网页的RenderObjectTree与RenderLayerTree的关系可以通过图1描述,如下所示:



图1RenderLayerTree与DOMTree、RenderObjectTree和GraphicsLayerTree的关系



从图1还可以看到,RenderLayerTree创建完成之后,WebKit还会继续创建一个GraphicsLayerTree。本文主要关注RenderLayerTree的创建过程。DOMTree和RenderObjectTree的创建过程可以参考和这两篇文章。GraphicsLayerTree的创建过程在接下来一篇文章分析。



网页的RenderLayerTree是在创建RenderObjectTree的过程中创建的。确切地说,是在设置RenderObject的CSS属性的过程中创建的。从前面一文可以知道,当DOMTree中的HTMLElement节点需要进行渲染的时候,WebKit就会为其创建一个RenderObject。这个RenderObject在创建完成之后,就会被设置CSS属性,如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

voidRenderTreeBuilder::createRendererForElementIfNeeded()

{

......



Elementelement=toElement(m_node);

RenderStyle&style=this->style();



if(!element->rendererIsNeeded(style))

return;



RenderObjectnewRenderer=element->createRenderer(&style);

......



RenderObjectparentRenderer=this->parentRenderer();

......



element->setRenderer(newRenderer);

newRenderer->setStyle(&style);//setStyle()candependonrenderer()alreadybeingset.



parentRenderer->addChild(newRenderer,nextRenderer);

}

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/dom/RenderTreeBuilder.cpp中。

从这里可以看到,新创建的RenderObject的CSS属性是通过调用RenderObject类的成员函数setStyle设置的,它的实现如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

voidRenderObject::setStyle(PassRefPtrstyle)

{

......



if(m_style==style){

......

return;

}



StyleDifferencediff;

unsignedcontextSensitiveProperties=ContextSensitivePropertyNone;

if(m_style)

diff=m_style->visualInvalidationDiff(style,contextSensitiveProperties);



......



RefPtroldStyle=m_style.release();

setStyleInternal(style);



......



styleDidChange(diff,oldStyle.get());



......

}

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/RenderObject.cpp中。



RenderObject类的成员变量m_style指向的是一个RenderStyle对象,它描述的是当前正在处理的RenderObject对象的CSS属性。RenderObject类的成员函数setStyle首先比较成员变量m_style指向的RenderStyle对象和参数style指向的RenderStyle对象所描述的CSS属性是否是一样的。如果是一样的,那么就不再重新设置当前正在处理的RenderObject对象的CSS属性。



如果成员变量m_style指向的RenderStyle对象和参数style指向的RenderStyle对象所描述的CSS属性不一样。那么RenderObject类的成员函数setStyle接下来就会计算出它们的差异,并且调用成员函数setStyleInternal将参数style指向的RenderStyle对象保存在成员变量m_style中,作为当前正在处理的RenderObject对象的CSS属性,最后调用成员函数styleDidChange通知子类当前正在处理的RenderObject对象的CSS属性发生了变化。



在前面一文中,我们假设创建的RenderObject实际上是一个RenderBlockFlow对象,也就是当前正在处理的是一个RenderBlockFlow对象。RenderBlockFlow类是从RenderObject类继承下来的,并且重写了成员函数styleDidChange。因此,接下来RenderBlockFlow类的成员函数styleDidChange就会被调用。在调用的过程中,就会检查是否需要创建一个RenderLayer。



RenderBlockFlow类的成员函数styleDidChange的实现如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

voidRenderBlockFlow::styleDidChange(StyleDifferencediff,constRenderStyleoldStyle)

{

RenderBlock::styleDidChange(diff,oldStyle);



......

}

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/RenderBlockFlow.cpp中。

RenderBlockFlow类的成员函数styleDidChange又会调用父类RenderBlock的成员函数styleDidChange通知它当前正在处理的RenderObject的CSS属性发生了变化。



RenderBlock类的成员函数styleDidChange的实现如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

voidRenderBlock::styleDidChange(StyleDifferencediff,constRenderStyleoldStyle)

{

RenderBox::styleDidChange(diff,oldStyle);



......

}

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/RenderBlock.cpp中。

RenderBlock类的成员函数styleDidChange又会调用父类RenderBox的成员函数styleDidChange通知它当前正在处理的RenderObject的CSS属性发生了变化。



RenderBox类的成员函数styleDidChange的实现如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

voidRenderBox::styleDidChange(StyleDifferencediff,constRenderStyleoldStyle)

{

......



RenderBoxModelObject::styleDidChange(diff,oldStyle);



......

}

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/RenderBox.cpp中。

RenderBox类的成员函数styleDidChange又会调用父类RenderBoxModelObject的成员函数styleDidChange通知它当前正在处理的RenderObject的CSS属性发生了变化。



RenderBoxModelObject类就是用来描述在前面一文提到的CSSBoxModel的,它的成员函数styleDidChange是从父类RenderLayerModelObject继承下来的,因此接下来我们继续分析RenderLayerModelObject类的成员函数styleDidChange的实现,如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

voidRenderLayerModelObject::styleDidChange(StyleDifferencediff,constRenderStyleoldStyle)

{

......



LayerTypetype=layerTypeRequired();

if(type!=NoLayer){

if(!layer()&&layerCreationAllowedForSubtree()){

......



createLayer(type);



......

}

}elseif(layer()&&layer()->parent()){

......



layer()->removeOnlyThisLayer();//callsdestroyLayer()whichclearsm_layer



......

}



if(layer()){

......



layer()->styleChanged(diff,oldStyle);

}



......

}

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/RenderLayerModelObject.cpp中。

RenderLayerModelObject类的成员函数styleDidChange首先调用成员函数layerTypeRequired判断是否需要为当前正在处理的RenderObject创建一个RenderLayer。如果需要创建,那么RenderLayerModelObject类的成员函数layerTypeRequired的返回值就不等于NoLayer。在这种情况下,RenderLayerModelObject类的成员函数就会调用成员函数layer检查当前正在处理的RenderObject是否已经关联有一个RenderLayer了。



如果已经关联有,那么调用RenderLayerModelObject类的成员函数layer的返回值就不等于NULL。这时候就不需要为当前正在处理的RenderObject创建一个RenderLayer。如果还没有关联,那么RenderLayerModelObject类的成员函数styleDidChange继续调用成员函数layerCreationAllowedForSubtree判断在当前正在处理的RenderObject的祖先节点中,是否存在一个类型为RenderSVGHiddenContainer的RenderObject。



类型为RenderSVGHiddenContainer的RenderObject是不需要绘制的,并且它的子节点也是不需要绘制的。因此,如果在当前正在处理的RenderObject的祖先节点中存在一个类型为RenderSVGHiddenContainer的RenderObject,那么就不需要为当前正在处理的RenderObject创建一个RenderLayer了。



另一方面,如果此时在当前正在处理的RenderObject的祖先节点中不存在一个类型为RenderSVGHiddenContainer的RenderObject,那么就需要为当前正在处理的RenderObject创建一个RenderLayer,这是通过调用RenderLayerModelObject类的成员函数createLayer创建一个RenderLayer。



如果前面RenderLayerModelObject类的成员函数layerTypeRequired的返回值等于NoLayer,并且当前正在处理的RenderObject关联有RenderLayer,以及这个RenderLayer位于RenderLayerTree中,也就是这个RenderLayer有父节点,那么就需要将这个RenderLayer从RenderLayerTree中删除,这是通过调用RenderLayer类的成员函数removeOnlyThisLayer实现的。





最后,如果前面RenderLayerModelObject类的成员函数styleDidChange为当前正在处理的RenderObject创建了一个RenderLayer,或者当前正在处理的RenderObject本来就已经关联有一个RenderLayer,那么RenderLayerModelObject类的成员函数styleDidChange还会通知这个RenderLayer,它所关联的RenderObject的CSS属性发生了变化,这是通过调用RenderLayer类的成员函数styleChanged实现的。RenderLayer类的成员函数styleChanged在调用期间,会判断是否需要为当前正在处理的RenderLayer创建一个GraphicsLayer。创建出来的GraphicsLayer就会形成图1所示的GraphicsLayerTree。在接下来一篇文章中,我们再详细分析GraphicsLayerTree的创建过程。



接下来,我们继续分析RenderLayerModelObject类的成员函数layerTypeRequired和createLayer的实现,以便了解一个RenderObject在什么情况下创建一个RenderLayer,以及这个RenderLayer的创建过程。



前面提到,当前正在处理的RenderObject实际上是一个RenderBlockFlow对象。RenderBlockFlow类是从RenderBox类继承下来的,RenderBox类又是从RenderLayerModelObject类继承下来的,并且它重写了RenderLayerModelObject类的成员函数layerTypeRequired,因此,前面分析的RenderLayerModelObject类的成员函数styleDidChange实际上是调用了子类RenderBox类的成员函数layerTypeRequired判断是否需要为当前正在处理的RenderObject创建一个RenderLayer。



RenderBox类的成员函数layerTypeRequired的实现如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

classRenderBox:publicRenderBoxModelObject{

public:

......



virtualLayerTypelayerTypeRequired()constOVERRIDE

{

if(isPositioned()||createsGroup()||hasClipPath()||hasTransform()||hasHiddenBackface()||hasReflection()||style()->specifiesColumns()||!style()->hasAutoZIndex()||style()->shouldCompositeForCurrentAnimations())

returnNormalLayer;

if(hasOverflowClip())

returnOverflowClipLayer;



returnNoLayer;

}



......

};

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/RenderBox.h中。



一个RenderObject的CSS属性如果具有以下10种情况之一,那么就需要为它创建一个RenderLayer:



1.isPositioned:position属性值不等于默认值static;



2.createsGroup:设置有透明度(transparent)、遮罩(mask)、滤镜(filter)或者混合模式(mix-blend-mode);



3.hasClipPath:设置有剪切路径(clip-path);



4.hasTransform:设置有2D或者3D转换(matrix、translate、scale、rotate、skew、perspective);



5.hasHiddenBackface:隐藏背面(backface-visibility:hidden);



6.hasReflection:设置有倒影(box-reflect);



7.specifiesColumns:设置有列宽和列数(columns:column-widthcolumn-count);



8.!hasAutoZIndex:z-index属性值不等于默认值auto,即指定了z-index值;



9.shouldCompositeForCurrentAnimations:指定了不透明度(opacity)、变换(transform)或者滤镜(filter)动画;



10.hasOverflowClip:剪切溢出内容(overflow:hidden)。



其中,前9种情况创建的RenderLayer的类型为NormalLayer,第10种情况创建的RenderLayer的类型为OverflowClipLayer。这两种类型的RenderLayer在本质上并没有区别,将一个RenderLayer的类型设置为OverflowClipLayer只为Bookkeeping目的,即在需要的时候可以在RenderLayerTree中找到一个类型为OverflowClipLayer的RenderLayer做相应的处理。



RenderLayerModelObject类的成员函数createLayer的实现如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

voidRenderLayerModelObject::createLayer(LayerTypetype)

{

ASSERT(!m_layer);

m_layer=adoptPtr(newRenderLayer(this,type));

setHasLayer(true);

m_layer->insertOnlyThisLayer();

}

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/RenderLayerModelObject.cpp中。

RenderLayerModelObject类的成员函数createLayer首先是创建了一个RenderLayer对象,并且保存在成员变量m_layer中。这个RenderLayer对象描述的就是与当前正在处理的RenderObject关联的RenderLayer。RenderLayerModelObject类的成员函数createLayer接下来又调用另外一个成员函数setHasLayer将当前正在处理的RenderObject标记为是关联有RenderLayer的。



RenderLayer对象的创建过程,即RenderLayer类的构造函数的实现如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

RenderLayer::RenderLayer(RenderLayerModelObjectrenderer,LayerTypetype)

:m_layerType(type)

,......

,m_renderer(renderer)

,m_parent(0)

,......

{

......

}

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/RenderLayer.cpp中。



RenderLayer类的构造函数主要是将参数renderer和type描述的RenderObject和LayerType分别保存在成员变量m_renderer和m_layerType中,并且将成员变量m_parent的值初始化为0,表示当前正在创建的RenderLayer还未插入到网页的RenderLayerTree中。



回到RenderLayerModelObject类的成员函数createLayer中,它最后调用前面创建的RenderLayer对象的成员函数insertOnlyThisLayer将其插入到网页的RenderLayerTree中去,如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

voidRenderLayer::insertOnlyThisLayer()

{

if(!m_parent&&renderer()->parent()){

//Weneedtoconnectourselveswhenourrenderer()hasaparent.

//FindourenclosingLayerandaddourselves.

RenderLayerparentLayer=renderer()->parent()->enclosingLayer();

ASSERT(parwww.shanxiwang.netentLayer);

RenderLayerbeforeChild=!parentLayer->reflectionInfo()||parentLayer->reflectionInfo()->reflectionLayer()!=this?renderer()->parent()->findNextLayer(parentLayer,renderer()):0;

parentLayer->addChild(this,beforeChild);

}



......

}

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/RenderLayer.cpp中。

前面提到,RenderLayer类的成员变量m_parent等于0的时候,表示当前正在处理的RenderLayer还未插入到网页的RenderLayerTree中,这时候才有可能需要将其插入到网页的RenderLayerTree中去。这里说有可能,是因为还需要满足另外一个条件,就是当前正在处理的RenderLayer所关联的RenderObject已经插入到网页的RenderObjectTree中,也就是该RenderObject具有父节点。



RenderLayer类的成员函数insertOnlyThisLayer按照以下三个步骤将一个RenderLayer插入到网页的RenderLayerTree中去:



1.找到要插入的RenderLayer关联的RenderObject的父节点所对应的RenderLayer。这里找到的RenderLayer就作为要插入的RenderLayer的父RenderLayer。



2.找到要插入的RenderLayer在父RenderLayer的ChildList中的位置。



3.将要插入的RenderLayer设置为父RenderLayer的Child。



其中,第1步和第3步分别是通过调用RenderObject类的成员函数enclosingLayer和RenderLayer类的成员函数addChild实现的,接下来我们就继续分析它们的实现,以便了解网页的RenderLayerTree的创建过程。



RenderObject类的成员函数enclosingLayer的实现如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

RenderLayerRenderObject::enclosingLayer()const

{

for(constRenderObjectcurrent=this;current;current=current->parent()){

if(current->hasLayer())

returntoRenderLayerModelObject(current)->layer();

}

//FIXME:Weshouldremovetheonecallerthattriggersthiscaseandmake

//thisfunctionreturnareference.

ASSERT(!m_parent&&!isRenderView());

return0;

}

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/RenderObject.cpp中。

从这里可以看到,一个RenderObject如果没有关联有RenderLayer,那么它就与离其最近的关联有RenderLayer的父节点使用同一个RenderLayer。



RenderLayer类的成员函数addChild的实现如下所示:



[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

voidRenderLayer::addChild(RenderLayerchild,RenderLayerbeforeChild)

{

RenderLayerprevSibling=beforeChild?beforeChild->previousSibling():lastChild();

if(prevSibling){

child->setPreviousSibling(prevSibling);

prevSibling->setNextSibling(child);

ASSERT(prevSibling!=child);

}else

setFirstChild(child);



if(beforeChild){

beforeChild->setPreviousSibling(child);

child->setNextSibling(beforeChild);

ASSERT(beforeChild!=child);

}else

setLastChild(child);



child->m_parent=this;



......

}

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/RenderLayer.cpp中。

一个RenderLayer的子RenderLayer以链表方式进行组织。RenderLayer类的成员函数addChild将参数child描述的子RenderLayer插入在参数beforeChild描述的子RenderLayer的前面。如果没有指定BeforeChild,那么参数child描述的子RenderLayer就会插入在链表的最后。另一方面,如果指定的BeforeChild就是保存在链表的第一个位置,那么参数child描述的子RenderLayer就会取代它保存在链表的第一个位置。



最后,RenderLayer类的成员函数addChild还会将参数child描述的RenderLayer的父RenderLayer设置为当前正在处理的RenderLayer,从而完成将一个RenderLayer插入到网页的RenderLayerTree的操作。



至此,我们就分析完成网页的RenderLayerTree的创建过程了。

献花(0)
+1
(本文系网络学习天...首藏)