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的创建过程了。
|
|