zoukankan      html  css  js  c++  java
  • Chromium网页Layer Tree创建过程分析

           在Chromium中。WebKit会创建一个Graphics Layer Tree描写叙述网页。Graphics Layer Tree是和网页渲染相关的一个Tree。

    网页渲染终于由Chromium的CC模块完毕,因此CC模块又会依据Graphics Layer Tree创建一个Layer Tree。以后就会依据这个Layer Tree对网页进行渲染。本文接下来就分析网页Layer Tree的创建过程。

    老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注。

    《Android系统源代码情景分析》一书正在进击的程序猿网(http://0xcc0xcd.com)中连载,点击进入!

           从前面Chromium网页Graphics Layer Tree创建过程分析一文能够知道,网页的Graphics Layer Tree是依据Render Layer Tree创建的,Render Layer Tree又是依据Render Object Tree创建的。Graphics Layer Tree与Render Layer Tree、Render Layer Tree与Render Object Tree的节点是均是一对多的关系,然而Graphics Layer Tree与CC模块创建的Layer Tree的节点是一一相应的关系。如图1所看到的:


    图1 Graphics Layer Tree与CC Layer Tree的关系

         也就是说,每个Graphics Layer都相应有一个CC Layer。只是,Graphics Layer与CC Layer不是直接的一一相应的,它们是透过另外两个Layer才相应起来的。如图2所看到的:


    图2 Graphics Layer与CC Layer的相应关系

           中间的两个Layer各自是WebContentLayerImpl和WebLayerImpl,它们是属于Content层的对象。关于Chromium的层次划分。能够參考前面Chromium网页载入过程简要介绍和学习计划一文的介绍。Graphics Layer与CC Layer的相应关系,是在Graphics Layer的创建过程中建立起来的,接下来我们就通过源代码分析这样的相应关系的建立过程。

           从前面Chromium网页Graphics Layer Tree创建过程分析一文能够知道。Graphics Layer是通过调用GraphicsLayerFactoryChromium类的成员函数createGraphicsLayer创建的,例如以下所看到的:

    PassOwnPtr<GraphicsLayer> GraphicsLayerFactoryChromium::createGraphicsLayer(GraphicsLayerClient* client)  
    {  
        OwnPtr<GraphicsLayer> layer = adoptPtr(new GraphicsLayer(client));  
        ......  
        return layer.release();  
    }  
          这个函数定义在文件external/chromium_org/third_party/WebKit/Source/web/GraphicsLayerFactoryChromium.cpp中。

          參数client指向的实际上是一个CompositedLayerMapping对象,这个CompositedLayerMapping对象会用来构造一个Graphics Layer。Graphics Layer的构造过程,也就是GraphicsLayer类的构造函数的实现,例如以下所看到的:

    GraphicsLayer::GraphicsLayer(GraphicsLayerClient* client)
        : m_client(client)
        , ...... 
    {
        ......
    
        m_opaqueRectTrackingContentLayerDelegate = adoptPtr(new OpaqueRectTrackingContentLayerDelegate(this));
        m_layer = adoptPtr(Platform::current()->compositorSupport()->createContentLayer(m_opaqueRectTrackingContentLayerDelegate.get()));
        
        ......
    }
           这个函数定义在文件external/chromium_org/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp中。

           GraphicsLayer类的构造函数首先是将參数client指向的CompositedLayerMapping对象保存在成员变量m_client中。接着又创建了一个OpaqueRectTrackingContentLayerDelegate对象保存在成员变量opaqueRectTrackingContentLayerDelegate中。

           再接下来GraphicsLayer类的构造函数通过Platform类的静态成员函数current获得一个RendererWebKitPlatformSupportImpl对象。

    这个RendererWebKitPlatformSupportImpl对象定义在Content模块中,它实现了由WebKit定义的Platform接口,用来向WebKit层提供平台相关的实现。

           通过调用RendererWebKitPlatformSupportImpl类的成员函数compositorSupport能够获得一个WebCompositorSupportImpl对象。有了这个WebCompositorSupportImpl对象之后,就能够调用它的成员函数createContentLayer创建一个WebContentLayerImpl对象,而且保存在GraphicsLayer类的成员变量m_layer中。

           WebCompositorSupportImpl类的成员函数createContentLayer的实现例如以下所看到的:

    WebContentLayer* WebCompositorSupportImpl::createContentLayer(
        WebContentLayerClient* client) {
      return new WebContentLayerImpl(client);
    }
           这个函数定义在文件external/chromium_org/content/renderer/compositor_bindings/web_compositor_support_impl.cc中。

           从这里能够看到,WebCompositorSupportImpl类的成员函数createContentLayer创建了一个WebContentLayerImpl对象返回给调用者。

           WebContentLayerImpl对象的创建过程,即WebContentLayerImpl类的构造函数的实现。例如以下所看到的:

    WebContentLayerImpl::WebContentLayerImpl(blink::WebContentLayerClient* client)
        : client_(client), ...... {
      if (WebLayerImpl::UsingPictureLayer())
        layer_ = make_scoped_ptr(new WebLayerImpl(PictureLayer::Create(this)));
      else
        layer_ = make_scoped_ptr(new WebLayerImpl(ContentLayer::Create(this)));
      ......
    }
           这个函数定义在文件external/chromium_org/content/renderer/compositor_bindings/web_content_layer_impl.cc中。

           从前面的调用过程能够知道,參数client指向的实际上是一个OpaqueRectTrackingContentLayerDelegate对象,WebContentLayerImpl类的构造函数首先将它保存在成员变量client_中 。

           WebContentLayerImpl类的构造函数接下来调用WebLayerImpl类的静态成员函数UsingPictureLayer推断Render进程是否启用Impl Side Painting特性。

    假设启用的话,就会调用PictureLayer类的静态成员函数Create创建一个Picture Layer;否则的话,就会调用ContentLayer类的静态成员函数Create创建一个Content Layer。

    有了Picture Layer或者Content Layer之后,再创建一个WebLayerImpl对象,保存在WebContentLayerImpl类的成员变量layer_中。

           当Render进程设置了enable-impl-side-painting启动选项时,就会启用Impl Side Painting特性,也就是会在Render进程中创建一个Compositor线程。与Render进程中的Main线程一起协作完毕网页的渲染。在这样的情况下。Graphics Layer在绘制网页内容的时候,实际上仅仅是记录了绘制命令。

    这些绘制命令就记录在相应的Picture Layer中。

           还有一方面,假设Render进程没有设置enable-impl-side-painting启动选项。那么Graphics Layer在绘制网页内容的时候,就会通过Content Layer提供的一个Canvas真正地把网页内容相应的UI绘制在一个内存缓冲区中。

           不管是Picture Layer还是Content Layer。它们都是在cc::Layer类继承下来的。也就是说,它们相应于图2所看到的的CC Layer。

    只是,我们仅仅考虑Picture Layer的情况,因此接下来我们继续分析Picture Layer的创建过程。也就是PictureLayer类的静态成员函数Create的实现,例如以下所看到的:

    scoped_refptr<PictureLayer> PictureLayer::Create(ContentLayerClient* client) {
      return make_scoped_refptr(new PictureLayer(client));
    }
           这个函数定义在文件external/chromium_org/cc/layers/picture_layer.cc中。

           从这里能够看到。PictureLayer类的静态成员函数Create创建了一个PictureLayer对象返回给调用者。

           PictureLayer对象的创建过程,也就是PictureLayer类的构造函数的实现,例如以下所看到的:

    PictureLayer::PictureLayer(ContentLayerClient* client)
        : client_(client),
          pile_(make_scoped_refptr(new PicturePile())),
          ...... {
    }
           这个函数定义在文件external/chromium_org/cc/layers/picture_layer.cc中。

           从前面的调用过程能够知道,參数client指向的实际上是一个WebContentLayerImpl对象,PictureLayer类的构造函数将它保存在成员变量client_中。

           PictureLayer类的构造函数还做了另外一件重要的事情,就是创建了一个PicturePile对象,而且保存在成员变量pile_中。

    这个PicturePile对象是用来将Graphics Layer的绘制命令记录在Pictrue Layer中的。后面我们分析网页内容的绘制过程时就会看到这一点。

           回到WebContentLayerImpl类的构造函数中,它创建了一个Pictrue Layer之后,接下来就会以这个Pictrue Layer为參数,创建一个WebLayerImpl对象,例如以下所看到的:

    WebLayerImpl::WebLayerImpl(scoped_refptr<Layer> layer) : layer_(layer) {
      ......
    }
           这个函数定义在文件external/chromium_org/content/renderer/compositor_bindings/web_layer_impl.cc中。

           WebLayerImpl类的构造函数主要就是将參数layer描写叙述的一个PictrueLayer对象保存在成员变量layer_中。

           从前面Chromium网页Graphics Layer Tree创建过程分析一文还能够知道。Graphics Layer与Graphics Layer是通过GraphicsLayer类的成员函数addChild形成父子关系的(从而形成Graphics Layer Tree)。例如以下所看到的:

    void GraphicsLayer::addChild(GraphicsLayer* childLayer)
    {
        addChildInternal(childLayer);
        updateChildList();
    }
          这个函数定义在文件external/chromium_org/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp中。

          GraphicsLayer类的成员函数addChild首先调用成员函数addChildInternal将參数childLayer描写叙述的一个Graphics Layer作为当前正在处理的Graphics Layer的子Graphics Layer,例如以下所看到的:

    void GraphicsLayer::addChildInternal(GraphicsLayer* childLayer)
    {
        ......
    
        childLayer->setParent(this);
        m_children.append(childLayer);
    
        ......
    }
           这个函数定义在文件external/chromium_org/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp中。

           这一步运行完毕后,Graphics Layer之间就建立了父子关系。

    回到GraphicsLayer类的成员函数addChild中。它接下来还会调用另外一个成员函数updateChildList,用来在CC Layer之间建立父子关系。从而形CC Layer Tree。

           GraphicsLayer类的成员函数updateChildList的实现例如以下所看到的:

    void GraphicsLayer::updateChildList()
    {
        WebLayer* childHost = m_layer->layer();
        ......
    
        for (size_t i = 0; i < m_children.size(); ++i)
            childHost->addChild(m_children[i]->platformLayer());
    
        ......
    }
          这个函数定义在文件external/chromium_org/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp中。

          从前面的分析能够知道,GraphicsLayer类的成员变量m_layer指向的是一个WebContentLayerImpl对象,调用这个WebContentLayerImpl对象的成员函数layer获得的是一个WebLayerImpl对象,例如以下所看到的:

    blink::WebLayer* WebContentLayerImpl::layer() {
      return layer_.get();
    }
          这个函数定义在文件external/chromium_org/content/renderer/compositor_bindings/web_content_layer_impl.cc中。

          从前面的分析能够知道。WebContentLayerImpl类的成员变量layer_指向的是一个WebLayerImpl对象,因此WebContentLayerImpl类的成员函数layer返回的是一个WebLayerImpl对象。

          回到GraphicsLayer类的成员函数updateChildList中,它接下来调用GraphicsLayer类的成员函数platformLayer获得当前正在处理的Graphics Layer的全部子Graphics Layer相应的WebLayerImpl对象,例如以下所看到的:

    WebLayer* GraphicsLayer::platformLayer() const
    {
        return m_layer->layer();
    }
           这个函数定义在文件external/chromium_org/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp中。

           这些子Graphics Layer相应的WebLayerImpl对象也就是通过调用它们的成员变量m_layer指向的WebContentLayerImpl对象的成员函数layer获得的。

           再回到GraphicsLayer类的成员函数updateChildList中,获得当前正在处理的Graphics Layer相应的WebLayerImpl对象,以及其全部的子Graphics Layer相应的WebLayerImpl对象之后,就能够通过调用WebLayerImpl类的成员函数addChild在它们之间也建立父子关系,例如以下所看到的:

    void WebLayerImpl::addChild(WebLayer* child) {
      layer_->AddChild(static_cast<WebLayerImpl*>(child)->layer());
    }
           这个函数定义在文件external/chromium_org/content/renderer/compositor_bindings/web_layer_impl.cc中。

           从前面的分析能够知道,WebLayerImpl类的成员变量layer_指向的是一个PictrueLayer对象。因此WebLayerImpl类的成员函数addChild所做的事情就是在两个PictrueLayer对象之间建立父子关系,这是通过调用PictrueLayer类的成员函数AddChild实现的。

           PictrueLayer类的成员函数AddChild是父类Layer继承下来的。它的实现例如以下所看到的:

    void Layer::AddChild(scoped_refptr<Layer> child) {
      InsertChild(child, children_.size());
    }
           这个函数定义在文件external/chromium_org/cc/layers/layer.cc中。

           Layer类的成员函数AddChild将參数child描写叙述的Pictrue Layer设置为当前正在处理的Picture Layer的子Picture Layer,这是通过调用Layer类的成员函数InsertChild实现的。例如以下所看到的:

    void Layer::InsertChild(scoped_refptr<Layer> child, size_t index) {
      DCHECK(IsPropertyChangeAllowed());
      child->RemoveFromParent();
      child->SetParent(this);
      child->stacking_order_changed_ = true;
    
      index = std::min(index, children_.size());
      children_.insert(children_.begin() + index, child);
      SetNeedsFullTreeSync();
    }
          这个函数定义在文件external/chromium_org/cc/layers/layer.cc中。

          Layer类的成员函数InsertChild所做的第一件事情是将当前正在处理的Picture Layer设置为參数child描写叙述的Pictrue Layer的父Picture Layer,而且将參数child描写叙述的Pictrue Layer保存在当前正在处理的Picture Layer的子Picture Layer列表中。

          Layer类的成员函数InsertChild所做的第二件事情是调用另外一个成员函数SetNeedsFullTreeSync发出一个通知。要在CC Layer Tree与CC Pending Layer Tree之间做一个Tree结构同步。

          Layer类的成员函数SetNeedsFullTreeSync的实现例如以下所看到的:

    void Layer::SetNeedsFullTreeSync() {
      if (!layer_tree_host_)
        return;
    
      layer_tree_host_->SetNeedsFullTreeSync();
    }
          这个函数定义在文件external/chromium_org/cc/layers/layer.cc中。

          Layer类的成员变量layer_tree_host_指向的是一个LayerTreeHost对象,这个LayerTreeHost是用来管理CC Layer Tree的,后面我们再分析它的创建过程。

    Layer类的成员函数SetNeedsFullTreeSync所做的事情就是调用这个LayerTreeHost对象的成员函数SetNeedsFullTreeSync通知它CC Layer Tree结构发生了变化,须要将这个变化同步到CC Pending Layer Tree中去。

          LayerTreeHost类的成员函数SetNeedsFullTreeSync的实现例如以下所看到的:

    void LayerTreeHost::SetNeedsFullTreeSync() {
      needs_full_tree_sync_ = true;
      SetNeedsCommit();
    }
           这个函数定义在文件external/chromium_org/cc/trees/layer_tree_host.cc中。

           LayerTreeHost类的成员函数SetNeedsFullTreeSync将成员变量needs_full_tree_sync_设置为true。以标记要在CC Layer Tree和CC Pending Layer Tree之间做一次结构同步,然后再调用另外一个成员函数SetNeedsCommit请求在前面Chromium网页渲染机制简要介绍和学习计划一文中提到的调度器将CC Layer Tree同步到CC Pending Tree去。

    至于这个同步操作什么时候会运行,就是由调度器依据其内部的状态机决定了。这一点我们在后面的文章再分析。

           这一步运行完毕之后。就能够在CC模块中得到一个Layer Tree,这个Layer Tree与WebKit中的Graphics Layer Tree在结构上是全然同步的。而且这个同步过程是由WebKit控制的。这个同步过程之所以要由WebKit控制,是由于CC Layer Tree是依据Graphics Layer Tree创建的,而Graphics Layer Tree又是由WebKit管理的。

           WebKit如今还须要做的另外一件重要的事情是告诉CC模块。哪一个Picture Layer是CC Layer Tree的根节点,这样CC模块才干够对整个CC Layer Tree进行管理。非常显然,Graphics Layer Tree的根节点相应的Picture Layer。就是CC Layer Tree的根节点。因此,WebKit会在创建Graphics Layer Tree的根节点的时候,将该根节点相应的Picture Layer设置到CC模块中去。以便后者将其作为CC Layer Tree的根节点。

           Graphics Layer Tree的根节点是什么时候创建的呢?从前面Chromium网页载入过程简要介绍和学习计划这个系列的文章能够知道,Graphics Layer Tree的根节点相应于Render Layer Tree的根节点。Render Layer Tree的根节点又相应于Render Object Tree的根节点。因此我们就从Render Object Tree的根节点的创建过程開始。分析Graphics Layer Tree的根节点的创建过程。

           从前面Chromium网页DOM Tree创建过程分析一文能够知道,Render Object Tree的根节点是在Document类的成员函数attach中创建的。例如以下所看到的:

    void Document::attach(const AttachContext& context)
    {
        ......
    
        m_renderView = new RenderView(this);
        ......
    
        m_renderView->setStyle(StyleResolver::styleForDocument(*this));
    
        ......
    }
           这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/dom/Document.cpp中。

           Document类的成员函数attach首先创建了一个RenderView对象,保存在成员变量m_renderView中。这个RenderView对象就是Render Object Tree的根节点。Document类的成员函数attach接下来还会调用RenderView类的成员函数setStyle给前面创建的RenderView对象设置CSS属性。

           从前面Chromium网页Render Layer Tree创建过程分析一文能够知道,在给Render Object Tree的节点设置CSS属性的过程中,会创建相应的Render Layer。这一步发生在RenderLayerModelObject类的成员函数styleDidChange中,例如以下所看到的:

    void RenderLayerModelObject::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)  
    {  
        ......  
      
        LayerType type = layerTypeRequired();  
        if (type != NoLayer) {  
            if (!layer() && layerCreationAllowedForSubtree()) {  
                ......  
      
                createLayer(type);  
                
                ......  
            }  
        } else if (layer() && layer()->parent()) {  
            ......  
      
            layer()->removeOnlyThisLayer(); // calls destroyLayer() which clears m_layer  
      
            ......  
        }  
      
        if (layer()) {  
            ......  
      
            layer()->styleChanged(diff, oldStyle);  
        }  
      
        ......  
    }  
          这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/RenderLayerModelObject.cpp中。

          RenderLayerModelObject类的成员函数styleDidChange的具体分析能够參考Chromium网页Render Layer Tree创建过程分析一文。

    当中,Render Layer的创建是通过调用RenderLayerModelObject类的成员函数createLayer实现的。而且创建出来的Render Layer的成员函数styleChanged会被调用。用来设置它的CSS属性。

          在设置Render Layer Tree的根节点的CSS属性的过程中,会触发Graphics Layer Tree的根节点的创建。因此接下来我们继续分析RenderLayer类的成员函数styleChanged的实现,例如以下所看到的:

    void RenderLayer::styleChanged(StyleDifference diff, const RenderStyle* oldStyle)
    {
        ......
    
        m_stackingNode->updateStackingNodesAfterStyleChange(oldStyle);
    
        ......
    }
          这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/RenderLayer.cpp中。

          RenderLayer类的成员变量m_stackingNode指向的是一个RenderLayerStackingNode对象。

    这个RenderLayerStackingNode对象描写叙述的是一个Stacking Context。关于Stacking Context,能够參考前面Chromium网页Graphics Layer Tree创建过程分析一文。RenderLayer类的成员函数styleChanged调用上述RenderLayerStackingNode对象的成员函数updateStackingNodesAfterStyleChange通知它所关联的Render Layer的CSS属性发生了变化,这样它可能就须要更新自己的子元素。

          RenderLayerStackingNode类的成员函数updateStackingNodesAfterStyleChange的实现例如以下所看到的:

    void RenderLayerStackingNode::updateStackingNodesAfterStyleChange(const RenderStyle* oldStyle)
    {
        bool wasStackingContext = oldStyle ? !oldStyle->hasAutoZIndex() : false;
        int oldZIndex = oldStyle ?

    oldStyle->zIndex() : 0; bool isStackingContext = this->isStackingContext(); if (isStackingContext == wasStackingContext && oldZIndex == zIndex()) return; dirtyStackingContextZOrderLists(); if (isStackingContext) dirtyZOrderLists(); else clearZOrderLists(); }

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

           RenderLayerStackingNode类的成员函数updateStackingNodesAfterStyleChange推断当前正在处理的RenderLayerStackingNode对象关联的Render Layer的CSS属性变化,是否导致它从一个Stacking Context变为一个非Stacking Context。或者从一个非Stacking Context变为一个Stacking Context。

           在从非Stacking Context变为Stacking Context的情况下,RenderLayerStackingNode类的成员函数updateStackingNodesAfterStyleChange就会调用另外一个成员函数dirtyZOrderLists将Stacking Context标记为Dirty状态,这样以后在须要的时候就会依据该Stacking Context的子元素的z-index又一次构建Graphics Layer Tree。

           RenderLayerStackingNode类的成员函数dirtyZOrderLists的实现例如以下所看到的:

    void RenderLayerStackingNode::dirtyZOrderLists()
    {
        ......
    
        if (m_posZOrderList)
            m_posZOrderList->clear();
        if (m_negZOrderList)
            m_negZOrderList->clear();
        m_zOrderListsDirty = true;
    
        if (!renderer()->documentBeingDestroyed())
            compositor()->setNeedsCompositingUpdate(CompositingUpdateRebuildTree);
    }
           这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/RenderLayerStackingNode.cpp中。

           RenderLayerStackingNode类的成员函数dirtyZOrderLists首先是将用来保存子元素的两个列表清空。当中一个列表用来保存z-index为正数的子元素,还有一个列表用来保存z-index为负数的子元素。这些子元素在各自的列表中均是依照从小到大的顺序排列的。有了这个顺序之后。Graphics Layer Tree就能够方便地依照z-index顺序创建出来。

           RenderLayerStackingNode类的成员函数dirtyZOrderLists接下来将成员变量m_zOrderListsDirty的值设置为true,就将自己的状态标记为Dirty,以后就会又一次依据子元素的z-index值,将它们分别保存在相应的列表中。

           RenderLayerStackingNode类的成员函数dirtyZOrderLists最后推断当前载入的网页有没有被销毁。假设没有被销毁,就会调用另外一个成员函数compositor,获得一个RenderLayerCompositor对象。这个RenderLayerCompositor对象是用来管理当前载入的网页的Graphics Layer Tree的。

    有了这个RenderLayerCompositor对象之后。就能够调用它的成员函数setNeedsCompositingUpdate,用来通知它须要重建Graphics Layer Tree。

           RenderLayerCompositor类的成员函数setNeedsCompositingUpdate的实现例如以下所看到的:

    void RenderLayerCompositor::setNeedsCompositingUpdate(CompositingUpdateType updateType)
    {
        ......
        if (!m_renderView.needsLayout())
            enableCompositingModeIfNeeded();
    
        m_pendingUpdateType = std::max(m_pendingUpdateType, updateType);
        ......
    }
           这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/compositing/RenderLayerCompositor.cpp中。

           RenderLayerCompositor类的成员变量m_renderView描写叙述的是一个RenderView对象。

    这个RenderView对象就是在前面分析的Document类的成员函数attach中创建的RenderView对象。RenderLayerCompositor类的成员函数setNeedsCompositingUpdate推断它是否须要又一次Layout。

    假设须要的话,就会调用另外一个成员函数enableCompositingModeIfNeeded将网页的Render Layer Tree的根节点设置为一个Compositing Layer,也就是要为它创建一个Graphics Layer。

           在我们这个情景中,RenderLayerCompositor类的成员变量m_renderView描写叙述的RenderView对象是刚刚创建的,这意味它须要运行一个Layout操作。因此接下来RenderLayerCompositor类的成员函数setNeedsCompositingUpdate会调用成员函数enableCompositingModeIfNeeded为Render Layer Tree的根节点创建一个Graphics Layer。作为Graphics Layer Tree的根节点。

           RenderLayerCompositor类的成员函数enableCompositingModeIfNeeded的实现例如以下所看到的:

    void RenderLayerCompositor::enableCompositingModeIfNeeded()
    {
        ......
    
        if (rootShouldAlwaysComposite()) {
            ......
            setCompositingModeEnabled(true);
        }
    }
           这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/compositing/RenderLayerCompositor.cpp中。

           RenderLayerCompositor类的成员函数enableCompositingModeIfNeeded首先调用成员函数rootShouldAlwaysComposite推断是否要为网页Render Layer Tree的根节点创建一个Graphics Layer。假设须要的话,就调用另外一个成员函数setCompositingModeEnabled进行创建。

           RenderLayerCompositor类的成员函数rootShouldAlwaysComposite的实现例如以下所看到的:

    bool RenderLayerCompositor::rootShouldAlwaysComposite() const
    {
        if (!m_hasAcceleratedCompositing)
            return false;
        return m_renderView.frame()->isMainFrame() || m_compositingReasonFinder.requiresCompositingForScrollableFrame();
    }
           这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/compositing/RenderLayerCompositor.cpp中。

           仅仅有在採用硬件加速渲染网页的情况下。才须要创建Graphics Layer。当RenderLayerCompositor类的成员变量m_hasAcceleratedCompositing的值等于true的时候,就表示描写叙述网页採用硬件加速渲染。因此,当RenderLayerCompositor类的成员变量m_hasAcceleratedCompositing的值等于false的时候,RenderLayerCompositor类的成员函数就返回一个false值给调用者,表示不须要为网页Render Layer Tree的根节点创建Graphics Layer。

           在採用硬件加速渲染网页的情况下,在两种情况下,须要为Render Layer Tree的根节点创建Graphics Layer。第一种情况是当前网页载入在Main Frame中。另外一种情况是当前网页不是载入在Main Frame,比如是通过iframe嵌入在Main Frame中,可是它是可滚动的。

           我们假设当前网页是载入在Main Frame中的,因此RenderLayerCompositor类的成员函数rootShouldAlwaysComposite的返回值为true,这时候RenderLayerCompositor类的成员函数enableCompositingModeIfNeeded就会调用另外一个成员函数setCompositingModeEnabled为网页Render Layer Tree的根节点创建Graphics Layer。

           RenderLayerCompositor类的成员函数setCompositingModeEnabled的实现例如以下所看到的:

    void RenderLayerCompositor::setCompositingModeEnabled(bool enable)
    {
        ......
    
        m_compositing = enable;
    
        ......
    
        if (m_compositing)
            ensureRootLayer();
        else
            destroyRootLayer();
    
        ......
    }
           这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/compositing/RenderLayerCompositor.cpp中。

           从前面的调用过程能够知道,參数enable的值等于true,这时候RenderLayerCompositor类的成员函数setCompositingModeEnabled会调用另外一个成员函数ensureRootLayer创建Graphics Layer Tree的根节点。

           RenderLayerCompositor类的成员函数ensureRootLayer的实现例如以下所看到的:

    void RenderLayerCompositor::ensureRootLayer()  
    {  
        RootLayerAttachment expectedAttachment = m_renderView.frame()->isMainFrame() ? RootLayerAttachedViaChromeClient : RootLayerAttachedViaEnclosingFrame;
        ......  
      
        if (!m_rootContentLayer) {  
            m_rootContentLayer = GraphicsLayer::create(graphicsLayerFactory(), this);  
            ......  
        }  
      
        if (!m_overflowControlsHostLayer) {  
            ......  
      
            // Create a layer to host the clipping layer and the overflow controls layers.  
            m_overflowControlsHostLayer = GraphicsLayer::create(graphicsLayerFactory(), this);  
      
            // Create a clipping layer if this is an iframe or settings require to clip.  
            m_containerLayer = GraphicsLayer::create(graphicsLayerFactory(), this);  
            ......  
      
            m_scrollLayer = GraphicsLayer::create(graphicsLayerFactory(), this);  
            ......  
            // Hook them up  
            m_overflowControlsHostLayer->addChild(m_containerLayer.get());  
            m_containerLayer->addChild(m_scrollLayer.get());  
            m_scrollLayer->addChild(m_rootContentLayer.get());  
      
            ......  
        }  
      
        ......  
    
        attachRootLayer(expectedAttachment);
    }  

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

           RenderLayerCompositor类的成员函数ensureRootLayer的具体分析能够參考前面Chromium网页Graphics Layer Tree创建过程分析一文,如今我们关注的重点是它最后调用另外一个成员函数attachRootLayer将Graphics Layer Tree的根节点设置给WebKit的使用者。即Chromium的Content层。

           RenderLayerCompositor类的成员函数attachRootLayer的实现例如以下所看到的:

    void RenderLayerCompositor::attachRootLayer(RootLayerAttachment attachment)
    {
        ......
    
        switch (attachment) {
            ......
            case RootLayerAttachedViaChromeClient: {
                LocalFrame& frame = m_renderView.frameView()->frame();
                Page* page = frame.page();
                if (!page)
                    return;
                page->chrome().client().attachRootGraphicsLayer(rootGraphicsLayer());
                break;
            }
            ......
        }
    
        m_rootLayerAttachment = attachment;
    }
           这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/rendering/compositing/RenderLayerCompositor.cpp中。

           从前面的调用过程能够知道,假设当前网页是在Main Frame中载入的。那么參数attachment的值就等于RootLayerAttachedViaChromeClient,这时候RenderLayerCompositor类的成员函数attachRootLayer与当前载入网页关联的一个ChromeClientImpl对象。而且调用这个ChromeClientImpl对象的成员函数attachRootGraphicsLayer将Graphics Layer Tree的根节点传递给它处理。

    Graphics Layer Tree的根节点能够通过调用RenderLayerCompositor类的成员函数rootGraphicsLayer获得。

           ChromeClientImpl类的成员函数attachRootGraphicsLayer的实现例如以下所看到的:

    void ChromeClientImpl::attachRootGraphicsLayer(GraphicsLayer* rootLayer)
    {
        m_webView->setRootGraphicsLayer(rootLayer);
    }
           这个函数定义在文件external/chromium_org/third_party/WebKit/Source/web/ChromeClientImpl.cpp中。

           ChromeClientImpl类的成员变量m_webView指向的是一个WebViewImpl对象。这个WebViewImpl对象的创建过程能够參考前面Chromium网页Frame Tree创建过程分析一文。ChromeClientImpl类的成员函数attachRootGraphicsLayer所做的事情就是调用这个WebViewImpl对象的成员函数setRootGraphicsLayer,以便将Graphics Layer Tree的根节点传递给它处理。

           WebViewImpl类的成员函数setRootGraphicsLayer的实现例如以下所看到的:

    void WebViewImpl::setRootGraphicsLayer(GraphicsLayer* layer)
    {
        if (pinchVirtualViewportEnabled()) {
            PinchViewport& pinchViewport = page()->frameHost().pinchViewport();
            pinchViewport.attachToLayerTree(layer, graphicsLayerFactory());
            if (layer) {
                m_rootGraphicsLayer = pinchViewport.rootGraphicsLayer();
                m_rootLayer = pinchViewport.rootGraphicsLayer()->platformLayer();
                ......
            } 
            ......
        } else {
            m_rootGraphicsLayer = layer;
            m_rootLayer = layer ? layer->platformLayer() : 0;
            ......
        }
    
        setIsAcceleratedCompositingActive(layer);
        
        ......
    }
           这个函数定义在文件external/chromium_org/third_party/WebKit/Source/web/WebViewImpl.cpp中。

           假设浏览器设置了"enable-pinch-virtual-viewport"启动选项。调用WebViewImpl类的成员函数pinchVirtualViewportEnabled得到的返回值就会为true。

    这时候网页有两个Viewport,一个称为Inner Viewport,还有一个称为Outer Viewport。Outer Viewport有两个特性。第一个特性是它的大小不尾随页面进行缩放,第二个特性是fixed positioned元素的位置是依据它来计算的。这样的体验的特点是fixed positioned元素的位置不会随页面的缩放发生变化。为实现这样的体验,须要在Graphics Layer Tree中添加一些Graphics Layer。

    这些Graphics Layer通过一个PinchViewport管理。

    这时候Graphics Layer Tree的根节点就不再是參数layer描写叙述的Graphics Layer,而是PinchViewport额外创建的一个Graphics Layer。

    关于Pinch Virtual Viewport特性的很多其他信息。能够參考官方文档:Layer-based Solution for Pinch Zoom / Fixed Position

           为简单起见,我们假设没有设置"enable-pinch-virtual-viewport"启动选项。这时候WebViewImpl类的成员函数setRootGraphicsLayer会将參数layer指向的一个Graphics Layer,也就是Graphics Layer Tree的根节点,保存在成员变量m_rootGraphicsLayer中,而且调用它的成员函数platformLayer获得与它关联的一个WebLayerImpl对象,保存在另外一个成员变量m_rootLayer中。

           再接下来。WebViewImpl类的成员函数setRootGraphicsLayer调用另外一个成员函数setIsAcceleratedCompositingActive激活网页的硬件加速渲染,它的实现例如以下所看到的:

    void WebViewImpl::setIsAcceleratedCompositingActive(bool active)
    {
        ......
    
        if (!active) {
            m_isAcceleratedCompositingActive = false;
            ......
        } else if (m_layerTreeView) {
            m_isAcceleratedCompositingActive = true;
            ......
        } else {
            ......
    
            m_client->initializeLayerTreeView();
            m_layerTreeView = m_client->layerTreeView();
            if (m_layerTreeView) {
                m_layerTreeView->setRootLayer(*m_rootLayer);
                ......
            }
    
            ......
    
            m_isAcceleratedCompositingActive = true;
    
            ......
        }
    
        ......
    }

           这个函数定义在文件external/chromium_org/third_party/WebKit/Source/web/WebViewImpl.cpp中。

           从前面的调用过程能够知道。參数active的值是等于true的,WebViewImpl类的成员变量m_isAcceleratedCompositingActive的值将被设置为參数active的值,用来表示网页是否已经激活网页硬件加速渲染。

           WebViewImpl类还有两个重要的成员变量m_client和m_layerTreeView。当中。成员变量m_client的初始化过程能够參考前面Chromium网页Frame Tree创建过程分析一文。它指向的是一个在Chromium的Content层创建的RenderViewImpl对象。

          另外一个成员变量m_layerTreeView是一个类型为WebLayerTreeView指针,它的值開始的时候是等于NULL的。WebViewImpl类的成员函数setIsAcceleratedCompositingActive被调用的时候,假设參数active的值是等于true,而且成员变量m_layerTreeView的值也等于NULL。那么WebKit就会先请求使用者,也就是Chromium的Content层。初始化CC Layer Tree。

    这是通过调用成员变量m_client指向的一个RenderViewImpl对象的成员函数initializeLayerTreeView实现的。Layer Tree View初始化完毕之后,WebViewImpl类再将成员变量m_rootLayer描写叙述的WebLayerImpl对象关联的Picture Layer设置为CC Layer Tree的根节点。

           接下来我们先分析RenderViewImpl类的成员函数initializeLayerTreeView初始化CC Layer Tree的过程,然后再分析设置CC Layer Tree根节点的过程。

           RenderViewImpl类的成员函数initializeLayerTreeView的实现例如以下所看到的:

    void RenderViewImpl::initializeLayerTreeView() {
      RenderWidget::initializeLayerTreeView();
      ......
    }
           这个函数定义在文件external/chromium_org/content/renderer/render_view_impl.cc中。

           RenderViewImpl类的成员函数initializeLayerTreeView主要是调用父类RenderWidget的成员函数initializeLayerTreeView初始化一个CC Layer Tree,例如以下所看到的:

    void RenderWidget::initializeLayerTreeView() {
      compositor_ = RenderWidgetCompositor::Create(
          this, is_threaded_compositing_enabled_);
      ......
      if (init_complete_)
        StartCompositor();
    }
           这个函数定义在文件external/chromium_org/content/renderer/render_widget.cc中。

           RenderWidget类的成员函数initializeLayerTreeView首先是调用RenderWidgetCompositor类的静态成员函数Create创建一个RenderWidgetCompositor对象,而且保存在成员变量compositor_中。在创建这个RenderWidgetCompositor对象期间,也会伴随着创建一个CC Layer Tree。

           RenderWidget类有一个成员变量init_complete_,当它的值等于true的时候,表示Browser进程已经为当前正在载入的网页初始化好Render View,这时候RenderWidget类的成员函数initializeLayerTreeView就会调用另外一个成员函数StartCompositor激活前面Chromium网页载入过程简要介绍和学习计划一文中提到的调度器,表示它能够開始进行调度工作了。

           接下来我们先分析RenderWidget类的成员变量init_complete_被设置为true的过程。从前面Chromium网页Frame Tree创建过程分析一文能够知道,当Browser进程为在Render进程中载入的网页创建了一个Render View之后,会向Render进程发送一个类型为ViewMsg_New的消息。这个IPC消息被RenderThreadImpl类的成员函数OnCreateNewView处理。

    在处理期间,会创建一个RenderViewImpl对象,而且调用它的成员函数Initialize对其进行初始化,例如以下所看到的:

    void RenderViewImpl::Initialize(RenderViewImplParams* params) {  
      ......  
      
      main_render_frame_.reset(RenderFrameImpl::Create(  
          this, params->main_frame_routing_id));  
      ......  
      
      WebLocalFrame* web_frame = WebLocalFrame::create(main_render_frame_.get());  
      main_render_frame_->SetWebFrame(web_frame);  
      ......  
      
      webwidget_ = WebView::create(this);  
      ......  
    
      // If this is a popup, we must wait for the CreatingNew_ACK message before
      // completing initialization.  Otherwise, we can finish it now.
      if (opener_id_ == MSG_ROUTING_NONE) {
        ......
        CompleteInit();
      }
      
      webview()->setMainFrame(main_render_frame_->GetWebFrame());  
        
      ......  
    }
           这个函数定义在文件external/chromium_org/content/renderer/render_view_impl.cc中。

           RenderViewImpl类的成员函数Initialize的具体分析能够參考前面Chromium网页Frame Tree创建过程分析一文。这里我们看到,当RenderViewImpl类的成员变量opener_id_的值等于MSG_ROUTING_NONE的时候,另外一个成员函数CompleteInit就会被调用。RenderViewImpl类的成员变量opener_id_什么时候会等于MSG_ROUTING_NONE呢?假设正在载入的网页不是在一个Popup Window显示时,它的值就会等于MSG_ROUTING_NONE,否则它的值等于将它Popup出来的网页的Routing ID。

    从代码凝视我们还能够看到。假设当前载入的网页是在一个Popup Window显示时,RenderViewImpl类的成员函数CompleteInit将会延迟到Render进程接收到Broswer发送另外一个类型为ViewMsg_CreatingNew_ACK的IPC消息时才会被调用。

           我们假设正在载入的网页不是一个Popup Window显示,这时候RenderViewImpl类的成员函数CompleteInit就会被调用。RenderViewImpl类的成员函数CompleteInit是从父类RenderWidget继承下来的,它的实现例如以下所看到的:

    void RenderWidget::CompleteInit() {
      ......
    
      init_complete_ = true;
    
      if (compositor_)
        StartCompositor();
    
      ......
    }
           这个函数定义在文件external/chromium_org/content/renderer/render_widget.cc中。

           从这里就能够看到,RenderWidget类的成员变量init_complete_将会被设置为true,而且在成员变量compositor_的值不等于NULL的情况下。会调用前面提到的成员函数StartCompositor激活前面Chromium网页载入过程简要介绍和学习计划一文中提到的调度器。

           回到前面分析的RenderWidget类的成员函数initializeLayerTreeView中。我们假设正在载入的网页不是在一个Popup Window显示,因此当RenderWidget类的成员函数initializeLayerTreeView被调用时,Browser进程已经为正在载入的网页初始化好了Render View,这意味着此时RenderWidget类的成员变量init_complete_已经被设置为true,于是RenderWidget类的成员函数initializeLayerTreeView就会先调用RenderWidgetCompositor类的静态成员函数Create创建一个RenderWidgetCompositor对象,然后再调用另外一个成员函数StartCompositor激活前面Chromium网页载入过程简要介绍和学习计划一文中提到的调度器。接下来我们就先分析RenderWidgetCompositor类的静态成员函数Create的实现,在接下来一篇文章中再分析RenderWidget类的成员函数StartCompositor激活调度器的过程。

           RenderWidgetCompositor类的静态成员函数Create的实现例如以下所看到的:

    scoped_ptr<RenderWidgetCompositor> RenderWidgetCompositor::Create(
        RenderWidget* widget,
        bool threaded) {
      scoped_ptr<RenderWidgetCompositor> compositor(
          new RenderWidgetCompositor(widget, threaded));
    
      CommandLine* cmd = CommandLine::ForCurrentProcess();
    
      cc::LayerTreeSettings settings;
      ......
    
      settings.initial_debug_state.show_debug_borders =
          cmd->HasSwitch(cc::switches::kShowCompositedLayerBorders);
      settings.initial_debug_state.show_fps_counter =
          cmd->HasSwitch(cc::switches::kShowFPSCounter);
      settings.initial_debug_state.show_layer_animation_bounds_rects =
          cmd->HasSwitch(cc::switches::kShowLayerAnimationBounds);
      settings.initial_debug_state.show_paint_rects =
          cmd->HasSwitch(switches::kShowPaintRects);
      settings.initial_debug_state.show_property_changed_rects =
          cmd->HasSwitch(cc::switches::kShowPropertyChangedRects);
      settings.initial_debug_state.show_surface_damage_rects =
          cmd->HasSwitch(cc::switches::kShowSurfaceDamageRects);
      settings.initial_debug_state.show_screen_space_rects =
          cmd->HasSwitch(cc::switches::kShowScreenSpaceRects);
      settings.initial_debug_state.show_replica_screen_space_rects =
          cmd->HasSwitch(cc::switches::kShowReplicaScreenSpaceRects);
      settings.initial_debug_state.show_occluding_rects =
          cmd->HasSwitch(cc::switches::kShowOccludingRects);
      settings.initial_debug_state.show_non_occluding_rects =
          cmd->HasSwitch(cc::switches::kShowNonOccludingRects);
      ......
    
      compositor->Initialize(settings);
    
      return compositor.Pass();
    }
           这个函数定义在文件external/chromium_org/content/renderer/gpu/render_widget_compositor.cc中。

           RenderWidgetCompositor类的静态成员函数Create首先创建一个RenderWidgetCompositor对象。接着依据Render进程的启动选项初始化一个LayerTreeSettings对象,最后以这个LayerTreeSettings对象为參数,对前面创建的RenderWidgetCompositor对象进行初始化。这是通过调用RenderWidgetCompositor类的成员函数Initialize实现的。

           接下来我们先分析RenderWidgetCompositor对象的创建过程,也就是RenderWidgetCompositor类的构造函数的实现。接下来再分析RenderWidgetCompositor对象的初始化过程,也就是RenderWidgetCompositor类的成员函数Initialize的实现。

           RenderWidgetCompositor类的构造函数的实现例如以下所看到的:

    RenderWidgetCompositor::RenderWidgetCompositor(RenderWidget* widget,
                                                   bool threaded)
        : threaded_(threaded),
          ......,
          widget_(widget) {
    }
          这个函数定义在文件external/chromium_org/content/renderer/gpu/render_widget_compositor.cc中。

          RenderWidgetCompositor类的构造函数主要是将參数widget指向的RenderViewImpl对象保存在成员变量widget_中,而且将參数threaded的值保存在成员变量threaded_中。參数threaded用来描写叙述Render进程是否要採用线程化渲染,也就是是否须要创建一个Compositor线程来专门运行渲染相关的工作。

          从前面的调用过程能够知道。參数threaded是从RenderWidget类的成员函数initializeLayerTreeView中传递过来的。它的值等于RenderWidget类的成员变量is_threaded_compositing_enabled_的值。RenderWidget类的成员变量is_threaded_compositing_enabled_是在构造函数初始化的。例如以下所看到的:

    RenderWidget::RenderWidget(blink::WebPopupType popup_type,
                               const blink::WebScreenInfo& screen_info,
                               bool swapped_out,
                               bool hidden,
                               bool never_visible)
        : ...... {
      ......
      is_threaded_compositing_enabled_ =
          CommandLine::ForCurrentProcess()->HasSwitch(
              switches::kEnableThreadedCompositing);
    }
          这个函数定义在文件external/chromium_org/content/renderer/render_widget.cc中。

          这意味着当Render进程设置了“enable-threaded-compositing”启动选项时,Render进程就会採用线程化渲染机制。我们接下来以及以后的文章仅仅考虑线程化渲染机制这样的情况。

          回到RenderWidgetCompositor类的构造函数中,这意味着它的成员变量threaded_会被设置为true。

          接下来我们继续分析RenderWidgetCompositor对象的初始化过程,也就是RenderWidgetCompositor类的成员函数Initialize的实现,例如以下所看到的:

    void RenderWidgetCompositor::Initialize(cc::LayerTreeSettings settings) {
      scoped_refptr<base::MessageLoopProxy> compositor_message_loop_proxy;
      RenderThreadImpl* render_thread = RenderThreadImpl::current();
      ......
      // render_thread may be NULL in tests.
      if (render_thread) {
        compositor_message_loop_proxy =
            render_thread->compositor_message_loop_proxy();
        ......
      }
      if (compositor_message_loop_proxy.get()) {
        layer_tree_host_ = cc::LayerTreeHost::CreateThreaded(
            this, shared_bitmap_manager, settings, compositor_message_loop_proxy);
      } else {
        layer_tree_host_ = cc::LayerTreeHost::CreateSingleThreaded(
            this, this, shared_bitmap_manager, settings);
      }
      ......
    }
           这个函数定义在文件external/chromium_org/content/renderer/gpu/render_widget_compositor.cc中。

           RenderWidgetCompositor类的成员函数Initialize首先调用RenderThreadImpl类的静态成员函数current获得一个RenderThreadImpl对象。这个RenderThreadImpl对象描写叙述的实际上是Render进程的Render线程,也就是Main线程。

    这个Main线程是在Render进程启动的时候创建的。是一定会存在的,具体能够參考Chromium的Render进程启动过程分析一文。

           RenderWidgetCompositor类的成员函数Initialize接下来调用前面获得的RenderThreadImpl对象的成员函数compositor_message_loop_proxy获得Render进程的Compositor线程的消息循环代理对象。

    当这个消息循环代理对象存在时,就会调用cc::LayerTreeHost类的静态成员函数CreateThreaded创建一个支持线程化渲染的LayerTreeHost对象,而且保存在成员变量layer_tree_host_中。还有一方面,假设Render进程中不存在Compositor线程。那么RenderWidgetCompositor类的成员函数Initialize就会调用cc::LayerTreeHost类的静态成员函数CreateSingleThreaded创建一个不支持线程化渲染的LayerTreeHost对象。在后一种情况下,全部的渲染操作都将在Render进程的Main线程中运行。

           接下来我们首先分析Render进程的Compositor线程的创建过程。前面提到。当Browser进程为Render进程中载入的网页创建了一个Render View时,就会发送一个类型为ViewMsg_New的IPC消息给Render进程。

    Render进程通过RenderThreadImpl类的成员函数OnCreateNewView接收和处理该消息。例如以下所看到的:

    void RenderThreadImpl::OnCreateNewView(const ViewMsg_New_Params& params) {  
      EnsureWebKitInitialized();  
      // When bringing in render_view, also bring in webkit's glue and jsbindings.  
      RenderViewImpl::Create(params.opener_route_id,  
                             params.window_was_created_with_opener,  
                             params.renderer_preferences,  
                             params.web_preferences,  
                             params.view_id,  
                             params.main_frame_routing_id,  
                             params.surface_id,  
                             params.session_storage_namespace_id,  
                             params.frame_name,  
                             false,  
                             params.swapped_out,  
                             params.proxy_routing_id,  
                             params.hidden,  
                             params.never_visible,  
                             params.next_page_id,  
                             params.screen_info,  
                             params.accessibility_mode);  
    }  
           这个函数定义在文件external/chromium_org/content/renderer/render_thread_impl.cc中。

           除了调用RenderViewImpl类的静态成员函数Create创建一个RenderViewImpl对象,RenderThreadImpl类的成员函数OnCreateNewView还会调用另外一个成员函数EnsureWebKitInitialized确保WebKit已经初始化好。

           RenderThreadImpl类的成员函数EnsureWebKitInitialized的实现例如以下所看到的:

    void RenderThreadImpl::EnsureWebKitInitialized() {
      if (webkit_platform_support_)
        return;
    
      webkit_platform_support_.reset(new RendererWebKitPlatformSupportImpl);
      blink::initialize(webkit_platform_support_.get());
      ......
    
      const CommandLine& command_line = *CommandLine::ForCurrentProcess();
    
      bool enable = command_line.HasSwitch(switches::kEnableThreadedCompositing);
      if (enable) {
        ......
    
        if (!compositor_message_loop_proxy_.get()) {
          compositor_thread_.reset(new base::Thread("Compositor"));
          compositor_thread_->Start();
          ......
          compositor_message_loop_proxy_ =
              compositor_thread_->message_loop_proxy();
          ......
        }
    
        ......
      }
    
      ......
    }
          这个函数定义在文件external/chromium_org/content/renderer/render_thread_impl.cc中。

          除了运行初始化WebKit的工作,RenderThreadImpl类的成员函数EnsureWebKitInitialized还做的另外一件重要事情是检查Render进程是否设置了“enable-threaded-compositing”启动选项。

    假设设置了,那么就会创建和启动一个Compositor线程,而且将这个Compositor线程的消息循环代理对象保存在成员变量compositor_message_loop_proxy_中。这样当RenderThreadImpl类的成员函数compositor_message_loop_proxy被调用时。Compositor线程的消息循环代理对象就会被返回给调用者。例如以下所看到的:

    class CONTENT_EXPORT RenderThreadImpl : public RenderThread,
                                            public ChildThread,
                                            public GpuChannelHostFactory {
     public:
      ......
    
      scoped_refptr<base::MessageLoopProxy> compositor_message_loop_proxy() const {
        return compositor_message_loop_proxy_;
      }
    
      ......
    };
          这个函数定义在文件external/chromium_org/content/renderer/render_thread_impl.h中。

          有了这个消息循环代理对象之后,就能够向Compositor线程发送消息请求其运行相应的操作了。

          回到RenderWidgetCompositor类的成员函数Initialize,它获得了Compositor线程的消息循环代理对象之后。接下来就调用cc::LayerTreeHost类的静态成员函数CreateThreaded创建一个支持线程化渲染的LayerTreeHost对象,例如以下所看到的:

    scoped_ptr<LayerTreeHost> LayerTreeHost::CreateThreaded(
        LayerTreeHostClient* client,
        SharedBitmapManager* manager,
        const LayerTreeSettings& settings,
        scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner) {
      DCHECK(impl_task_runner);
      scoped_ptr<LayerTreeHost> layer_tree_host(
          new LayerTreeHost(client, manager, settings));
      layer_tree_host->InitializeThreaded(impl_task_runner);
      return layer_tree_host.Pass();
    }
          这个函数定义在文件external/chromium_org/cc/trees/layer_tree_host.cc中。

          LayerTreeHost类的静态成员函数CreateThreaded首先创建了一个LayerTreeHost对象。例如以下所看到的:

    LayerTreeHost::LayerTreeHost(LayerTreeHostClient* client,
                                 SharedBitmapManager* manager,
                                 const LayerTreeSettings& settings)
        : ......,
          client_(client),
          ...... {
      ......
    }
          这个函数定义在文件external/chromium_org/cc/trees/layer_tree_host.cc中。

          从前面的调用过程能够知道,參数client指向的是一个RenderWidgetCompositor对象,这个RenderWidgetCompositor对象将会被保存LayerTreeHost类的成员变量client_中。

          回到LayerTreeHost类的静态成员函数CreateThreaded中。它创建了一个LayerTreeHost对象之后,接下来会调用它的成员函数InitializeThreaded对其进行初始化,例如以下所看到的:

    void LayerTreeHost::InitializeThreaded(
        scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner) {
      InitializeProxy(ThreadProxy::Create(this, impl_task_runner));
    }
          这个函数定义在文件external/chromium_org/cc/trees/layer_tree_host.cc中。

          从前面的调用过程能够知道,參数impl_task_runner描写叙述的是Compositor线程的消息循环,LayerTreeHost类的成员函数InitializeThreaded首先调用ThreadProxy类的静态成员函数Create创建一个ThreadProxy对象。接着用这个ThreadProxy对象初始化当前正在处理的LayerTreeHost对象,这是通过调用LayerTreeHost类的成员函数IntializeProxy实现的。

          接下来我们先分析ThreadProxy类的静态成员函数Create创建ThreadProxy对象的过程,接着再分析LayerTreeHost类的成员函数IntializeProxy初始化LayerTreeHost对象的过程。

          ThreadProxy类的静态成员函数Create的实现例如以下所看到的:

    scoped_ptr<Proxy> ThreadProxy::Create(
        LayerTreeHost* layer_tree_host,
        scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner) {
      return make_scoped_ptr(new ThreadProxy(layer_tree_host, impl_task_runner))
          .PassAs<Proxy>();
    }
          这个函数定义在文件external/chromium_org/cc/trees/thread_proxy.cc中。

          从这里能够看到,ThreadProxy类的静态成员函数Create创建了一个ThreadProxy对象返回给调用者。

          ThreadProxy对象的创建过程,即ThreadProxy类的构造函数的实现,例如以下所看到的:

    ThreadProxy::ThreadProxy(
        LayerTreeHost* layer_tree_host,
        scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner)
        : Proxy(impl_task_runner),
          ...... {
      ......
    }
          这个函数定义在文件external/chromium_org/cc/trees/thread_proxy.cc中。

          ThreadProxy类的构造函数主要是调用了父类Proxy的构造函数运行初始化工作,例如以下所看到的:

    Proxy::Proxy(scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner)
        : main_task_runner_(base::MessageLoopProxy::current()),
          ......
          impl_task_runner_(impl_task_runner),
          ...... {
      ......
    }
           这个函数定义在文件external/chromium_org/cc/trees/proxy.cc中。

           从前面的分析能够知道,參数impl_task_runner描写叙述的是Compositor线程的消息循环,它将被保存在Proxy类的成员变量impl_task_runner_中。此外,Proxy类的构造函数还会通过调用MessageLoopProxy类的静态成员函数current获得当前线程的消息循环,而且保存在成员变量main_task_runner_。当前线程即为Render进程的Main线程,因此Proxy类的成员变量main_task_runner_描写叙述的Main线程的消息循环。

           

           初始化好Proxy类的成员变量main_task_runner_和impl_task_runner_之后。以后就能够通过调用Proxy类的成员函数MainThreadTaskRunner和ImplThreadTaskRunner获得它们描写叙述的线程的消息循环,例如以下所看到的:

    base::SingleThreadTaskRunner* Proxy::MainThreadTaskRunner() const {
      return main_task_runner_.get();
    }
    
    ......
    
    base::SingleThreadTaskRunner* Proxy::ImplThreadTaskRunner() const {
      return impl_task_runner_.get();
    }
           这两个函数定义在文件external/chromium_org/cc/trees/proxy.cc中。      

           回到LayerTreeHost类的成员函数InitializeThreaded中,它创建了一个ThreadProxy对象之后。接下来就会调用另外一个成员函数InitializeProxy启动前面创建的ThreadProxy对象,例如以下所看到的:

    void LayerTreeHost::InitializeProxy(scoped_ptr<Proxy> proxy) {
      ......
    
      proxy_ = proxy.Pass();
      proxy_->Start();
    }
           这个函数定义在文件external/chromium_org/cc/trees/layer_tree_host.cc中。

           LayerTreeHost类的成员函数InitializeProxy首先将參数proxy描写叙述的一个ThreadProxy对象保存在成员变量proxy_中,接着再调用上述ThreadProxy对象的成员函数Start对它进行启动,例如以下所看到的:

    void ThreadProxy::Start() {
      ......
    
      CompletionEvent completion;
      Proxy::ImplThreadTaskRunner()->PostTask(
          FROM_HERE,
          base::Bind(&ThreadProxy::InitializeImplOnImplThread,
                     base::Unretained(this),
                     &completion));
      completion.Wait();
    
      main_thread_weak_ptr_ = main().weak_factory.GetWeakPtr();
    
      main().started = true;
    }
           这个函数定义在文件external/chromium_org/cc/trees/thread_proxy.cc中。

           ThreadProxy类的成员函数Start主要是向Compositor线程的消息队列发送了一个Task,而且等待这个Task完毕。这个Task绑定了ThreadProxy类的成员函数InitializeImplOnImplThread,因此接下来ThreadProxy类的成员函数InitializeImplOnImplThread就会在Compositor线程中运行,例如以下所看到的:

    void ThreadProxy::InitializeImplOnImplThread(CompletionEvent* completion) {
      ......
      impl().layer_tree_host_impl =
          layer_tree_host()->CreateLayerTreeHostImpl(this);
    
      SchedulerSettings scheduler_settings(layer_tree_host()->settings());
      impl().scheduler = Scheduler::Create(this,
                                           scheduler_settings,
                                           impl().layer_tree_host_id,
                                           ImplThreadTaskRunner());
      ......
    
      impl_thread_weak_ptr_ = impl().weak_factory.GetWeakPtr();
      completion->Signal();
    }
          这个函数定义在文件external/chromium_org/cc/trees/thread_proxy.cc中。

          ThreadProxy类的成员函数InitializeImplOnImplThread主要是做了三件事情。

          第一件事情是调用前面创建的LayerTreeHost对象的成员函数CreateLayerTreeHostImpl函数创建了一个LayerTreeHostImpl对象,而且保存在内部的一个CompositorThreadOnly对象的成员变量layer_tree_host_impl中。

    前面创建的LayerTreeHost对象能够通过调用成员函数layer_tree_host获得。

    内部的CompositorThreadOnly对象能够通过调用成员函数impl获得。创建出来的LayerTreeHostImpl对象以后负责管理CC Pending Layer Tree和CC Active Layer Tree。

          第二件事情是调用Scheduler类的静态成员函数Create创建了一个Scheduler对象。这个Scheduler对象以后就负责在Main线程与Compositor线程之间调度渲染工作。

          第三件事情是将參数completion描写叙述的Completion Event设置为有信号,这样正在等待的Main线程就能够唤醒继续运行其他工作了。

          接下来我们继续分析LayerTreeHost类的成员函数CreateLayerTreeHostImpl创建LayerTreeHostImpl对象的过程。例如以下所看到的:

    scoped_ptr<LayerTreeHostImpl> LayerTreeHost::CreateLayerTreeHostImpl(
        LayerTreeHostImplClient* client) {
      ......
      scoped_ptr<LayerTreeHostImpl> host_impl =
          LayerTreeHostImpl::Create(settings_,
                                    client,
                                    proxy_.get(),
                                    rendering_stats_instrumentation_.get(),
                                    shared_bitmap_manager_,
                                    id_);
      host_impl->SetUseGpuRasterization(UseGpuRasterization());
      ......
      return host_impl.Pass();
    }
           这个函数定义在文件external/chromium_org/cc/trees/layer_tree_host.cc中。

           LayerTreeHost类的成员函数CreateLayerTreeHostImpl首先是调用LayerTreeHostImpl类的静态成员函数Create创建了一个LayerTreeHostImpl对象,接着再调用这个LayerTreeHostImpl对象的成员函数SetUseGpuRasterization设置它是否使用GPU光栅化。

           LayerTreeHostImpl对象的创建过程。即LayerTreeHostImpl类的构造函数的实现,例如以下所看到的:

    LayerTreeHostImpl::LayerTreeHostImpl(
        const LayerTreeSettings& settings,
        LayerTreeHostImplClient* client,
        Proxy* proxy,
        RenderingStatsInstrumentation* rendering_stats_instrumentation,
        SharedBitmapManager* manager,
        int id)
        : client_(client),
          proxy_(proxy),
          ...... {
    
      .......
    
      // LTHI always has an active tree.
      active_tree_ = LayerTreeImpl::create(this);
      
      ......
    }
           这个函数定义在文件external/chromium_org/cc/trees/layer_tree_host_impl.cc中。

           从前面的调用过程能够知道,參数client和proxy指向的是同一个ThreadProxy对象。它们分别保存在LayerTreeHostImpl类的成员变量client_和proxy_中。LayerTreeHostImpl类的构造函数接下来还调用LayerTreeImpl类的静态成员函数create创建了一个Active Layer Tree。注意,这时候Pending Layer Tree还没有创建。等到要将Layer Tree内容同步到Pending Layer Tree的时候才会创建。

           回到LayerTreeHost类的成员函数CreateLayerTreeHostImpl,它创建了一个LayerTreeHostImpl对象之后,接下来调用另外一个成员函数UseGpuRasterization获取Render进程是否要採用GPU光栅化的信息。例如以下所看到的:

    bool LayerTreeHost::UseGpuRasterization() const {
      if (settings_.gpu_rasterization_forced) {
        return true;
      } else if (settings_.gpu_rasterization_enabled) {
        return has_gpu_rasterization_trigger_ &&
               content_is_suitable_for_gpu_rasterization_;
      } else {
        return false;
      }
    }
           这个函数定义在文件external/chromium_org/cc/trees/layer_tree_host.cc中。

           当Render进程设置了"force-gpu-rasterization"和"enable-impl-side-painting"启动选项时,LayerTreeHost类的成员变量settings_指向的LayerTreeSettings对象的成员变量gpu_rasterization_forced的值就会等于true,这时候就会强制使用GPU光栅化。

           当Render进程设置了"enable-gpu-rasterization"启动选项时,LayerTreeHost类的成员变量settings_指向的LayerTreeSettings对象的成员变量gpu_rasterization_enabled的值就会等于true,这时候是否使用GPU光栅化取决于LayerTreeHost类另外两个成员变量has_gpu_rasterization_trigger_和content_is_suitable_for_gpu_rasterization_。

    当这两个成员变量的值均等于true的时候,就会使作GPU光栅化。

           LayerTreeHost类的成员变量has_gpu_rasterization_trigger_的值由WebKit间接调用LayerTreeHost类的成员函数SetHasGpuRasterizationTrigger进行设置,这一点能够參考WebViewImpl类的成员函数updatePageDefinedViewportConstraints,主要是与网页的Viewport大小以及当前的缩放因子有关。

    LayerTreeHost类的成员变量content_is_suitable_for_gpu_rasterization_的值由Skia决定,后者依据当前要绘制的内容决定的,比如。假设要绘制的内容包括太多的凹多边形。而且这些凹多边形使用了反锯齿效果。那么就会禁用GPU光栅化。

    这一点能够參考SkPicturePlayback类的成员函数suitableForGpuRasterization。

           在其他情况下,都是禁止使用GPU光栅化的。在眼下的版本号中。Render进程设置了"force-gpu-rasterization"和"enable-impl-side-painting"启动选项,因此会强制使用GPU光栅化。

    在以后的文章中,我们都是假设Chromium是使用GPU光栅化的。

           再回到LayerTreeHost类的成员函数CreateLayerTreeHostImpl,它获得Render进程是否要採用GPU光栅化的信息之后,就会设置给前面创建的LayerTreeHostImpl对象,这是通过调用LayerTreeHostImpl类的成员函数SetUseGpuRasterization例如以下所看到的:       

    void LayerTreeHostImpl::SetUseGpuRasterization(bool use_gpu) {
      if (use_gpu == use_gpu_rasterization_)
        return;
    
      use_gpu_rasterization_ = use_gpu;
      ReleaseTreeResources();
    
      // Replace existing tile manager with another one that uses appropriate
      // rasterizer.
      if (tile_manager_) {
        DestroyTileManager();
        CreateAndSetTileManager();
      }
    
      // We have released tilings for both active and pending tree.
      // We would not have any content to draw until the pending tree is activated.
      // Prevent the active tree from drawing until activation.
      active_tree_->SetRequiresHighResToDraw();
    }
           这个函数定义在文件external/chromium_org/cc/trees/layer_tree_host_impl.cc中。

           LayerTreeHostImpl类的成员函数SetUseGpuRasterization会将參数use_gpu的值保存在成员变量use_gpu_rasterization_。

    假设这时候已经创建了分块管理器。即LayerTreeHostImpl类的成员变量tile_manager_的值不等于NULL,那么还会先销毁它,然后再又一次创建。由于分块管理器在创建的时候就决定了光栅化的方式。最后,LayerTreeHostImpl类的成员函数SetUseGpuRasterization还会要求又一次绘制高分辨率的Active Layer Tree。

           这一步运行完毕之后。CC Layer Tree就初始化完毕了。回到前面分析的WebViewImpl类的成员函数setIsAcceleratedCompositingActive中,它接下来会调用成员变量m_client指向的RenderViewImpl对象的成员函数layerTreeView获得之前创建的一个RenderWidgetCompositor对象。

           RenderViewImpl类的成员函数layerTreeView是从父类RenderWidget继承下来的。它的实现例如以下所看到的:

    blink::WebLayerTreeView* RenderWidget::layerTreeView() {
      return compositor_.get();
    }
           这个函数定义在文件external/chromium_org/content/renderer/render_widget.cc中。

           从这里能够看到,RenderWidget类的成员函数layerTreeView返回的是成员变量compositor_描写叙述的一个RenderWidgetCompositor对象。

           再回到前面分析的WebViewImpl类的成员函数setIsAcceleratedCompositingActive,它获得了一个RenderWidgetCompositor对象,就会调用这个RenderWidgetCompositor对象的成员函数setRootLayer,以便将CC Layer Tree的根节点传递给它处理,例如以下所看到的:

    void RenderWidgetCompositor::setRootLayer(const blink::WebLayer& layer) {
      layer_tree_host_->SetRootLayer(
          static_cast<const WebLayerImpl*>(&layer)->layer());
    }
           这个函数定义在文件external/chromium_org/content/renderer/gpu/render_widget_compositor.cc中。

           从前面的调用过程能够知道,參数layer描写叙述的实际上是一个WebLayerImpl对象,调用它的成员函数layer能够获得与它关联的一个PictureLayer对象。这个PictureLayer对象将作为CC Layer Tree的根节点,设置给RenderWidgetCompositor类的成员变量layer_tree_host_描写叙述的一个LayerTreeHost对象。

    这是通过调用LayerTreeHost类的成员函数SetRootLayer实现的。例如以下所看到的:

    void LayerTreeHost::SetRootLayer(scoped_refptr<Layer> root_layer) {
      ......
    
      root_layer_ = root_layer;
      ......
    
      SetNeedsFullTreeSync();
    }
           这个函数定义在文件external/chromium_org/cc/trees/layer_tree_host.cc中。

           从这里能够看到,LayerTreeHost类的成员函数SetRootLayer将CC Layer Tree的根节点保存在成员变量root_layer_中,以后通过这个成员变量就能够遍历整个CC Layer Tree。

    由于设置了新的CC Layer Tree,LayerTreeHost类的成员函数SetRootLayer还会调用另外一个成员函数SetNeedsFullTreeSync请求将CC Layer Tree同步到CC Pending Layer Tree去。

           前面我们已经分析过LayerTreeHost类的成员函数SetNeedsFullTreeSync的实现了,它调用了另外一个成员函数SetNeedsCommit请求将CC Layer Tree同步到CC Pending Layer Tree去,也就运行一次ACTION_COMMIT操作,后者的实现例如以下所看到的:

    void LayerTreeHost::SetNeedsCommit() {
      ......
      proxy_->SetNeedsCommit();
      ......
    }
           这个函数定义在文件external/chromium_org/cc/trees/layer_tree_host.cc中。

           LayerTreeHost类的成员函数SetNeedsCommit调用成员变量proxy_指向的一个ThreadProxy对象的成员函数SetNeedsCommit请求运行一次ACTION_COMMIT操作。例如以下所看到的:

    void ThreadProxy::SetNeedsCommit() {
      ......
    
      if (main().commit_requested)
        return;
      ......
      main().commit_requested = true;
    
      SendCommitRequestToImplThreadIfNeeded();
    }
          这个函数定义在文件external/chromium_org/cc/trees/thread_proxy.cc中。

          ThreadProxy对象的成员函数SetNeedsCommit首先推断之前请求的ACTION_COMMIT操作是否已经被运行。

    假设还没有被运行,那么就会忽略当前请求。实际上是将多个ACTION_COMMIT请求合成一个运行。其实。每当WebKit改动了Render Object Tree的内容时。都会请求CC模块运行一次ACTION_COMMIT操作。只是,假设这些ACTION_COMMIT操作请求得太过频繁,就会合成一个一起运行。

           假设之前没有请求过ACTION_COMMIT操作,或者之前请求的ACTION_COMMIT操作已经被运行,那么ThreadProxy类的成员函数SetNeedsCommit就会调用另外一个成员函数SendCommitRequestToImplThreadIfNeeded请求Compositor线程运行一次ACTION_COMMIT请求,例如以下所看到的:

    void ThreadProxy::SendCommitRequestToImplThreadIfNeeded() {
      DCHECK(IsMainThread());
      if (main().commit_request_sent_to_impl_thread)
        return; 
    
      main().commit_request_sent_to_impl_thread = true;
      Proxy::ImplThreadTaskRunner()->PostTask(
          FROM_HERE,
          base::Bind(&ThreadProxy::SetNeedsCommitOnImplThread,
                     impl_thread_weak_ptr_));
    }
           这个函数定义在文件external/chromium_org/cc/trees/thread_proxy.cc中。

           ThreadProxy类的成员函数SendCommitRequestToImplThreadIfNeeded必须要确保在Main线程中运行。这也是Chromium的多线程编程哲学。在设计上规定了某些对象仅仅能在特定的线程进行訪问。

           ThreadProxy类的成员函数SendCommitRequestToImplThreadIfNeeded首先推断Main线程之前是否已经向Compositor线程请求过运行ACTION_COMMIT操作。而且该请求已经被运行。假设是的话,那么就会忽略当前请求。否则的话,就会向Compositor线程的消息队列发送一个Task,这个Task绑定了ThreadProxy类的成员函数SetNeedsCommitOnImplThread。

           这意味着接下来ThreadProxy类的成员函数SetNeedsCommitOnImplThread接下来会在Compositor线程中运行。例如以下所看到的:

    void ThreadProxy::SetNeedsCommitOnImplThread() {
      ......
      DCHECK(IsImplThread());
      impl().scheduler->SetNeedsCommit();
    }
           这个函数定义在文件external/chromium_org/cc/trees/thread_proxy.cc中。

           Compositor线程并非立即就运行请求的ACTION_COMMIT操作,它仅仅是调用了内部的调度器的成员函数SetNeedsCommit,即Scheduler类的成员函数SetNeedsCommit,例如以下所看到的:

    void Scheduler::SetNeedsCommit() {
      state_machine_.SetNeedsCommit();
      ProcessScheduledActions();
    }
           这个函数定义在文件external/chromium_org/cc/scheduler/scheduler.cc中。

           Scheduler类的成员函数SetNeedsCommit首先通过调用成员变量state_machine_描写叙述的一个SchedulerStateMachine对象的成员函数SetNeedsCommit将内部的状态机设置为须要COMMIT,例如以下所看到的:

    void SchedulerStateMachine::SetNeedsCommit() { needs_commit_ = true; }
    
           这个函数定义在文件external/chromium_org/cc/scheduler/scheduler_state_machine.cc中。

           SchedulerStateMachine类的成员函数SetNeedsCommit仅仅是将成员变量needs_commit_的值设置为true,表示须要运行一次ACTION_COMMIT操作。只是,这个ACTION_COMMIT有可能不能立即运行。由于有可能CC模块还没有为网页创建Output Surface,或者CC模块如今须要对网页上一帧运行光栅化操作等等。调度器当前究竟须要运行什么样的操作。由Scheduler类的成员函数SetNeedsCommit调用另外一个成员函数ProcessScheduledActions决定。

           至此。我们就分析完毕Chromium为网页创建CC Layer Tree的过程了。有了网页的CC Layer Tree之后,Chromium还要为网页创建渲染上下文,也就是Output Surface,这样才干够将网页的内容渲染出来。网页Output Surface的创建以及网页其他渲染相关的操作。都是通过一个调度器依据一定的策略触发运行的。因此在接下来的一篇文章中,我们就继续分析Chromium网页渲染调度器的运行过程。敬请关注!很多其他的信息也能够关注老罗的新浪微博:http://weibo.com/shengyangluo

  • 相关阅读:
    超哥笔记--linux准备知识(1)
    爬虫系列---scrapy全栈数据爬取框架(Crawlspider)
    爬虫系列---scrapy post请求、框架组件和下载中间件+boss直聘爬取
    pymongo 一篇文章搞定
    一篇文章搞定mongodb
    python进阶(四) windows下虚拟环境使用
    java基础(四) -变量类型
    java基础(二) -对象和类
    java基础(一) -语法
    Linux常用命令大全
  • 原文地址:https://www.cnblogs.com/mfmdaoyou/p/7359418.html
Copyright © 2011-2022 走看看