Chromium网页DOM Tree创建过程分析.docx
- 文档编号:18113529
- 上传时间:2023-08-13
- 格式:DOCX
- 页数:38
- 大小:125.47KB
Chromium网页DOM Tree创建过程分析.docx
《Chromium网页DOM Tree创建过程分析.docx》由会员分享,可在线阅读,更多相关《Chromium网页DOM Tree创建过程分析.docx(38页珍藏版)》请在冰点文库上搜索。
Chromium网页DOMTree创建过程分析
Chromium网页DOMTree创建过程分析
在Chromium中,Render进程是通过Browser进程下载网页内容的,后者又是通过共享内存将下载回来的网页内容交给前者的。
Render进程获得网页内容之后,会交给WebKit进行处理。
WebKit所做的第一个处理就是对网页内容进行解析,解析的结果是得到一棵DOMTree。
DOMTree是网页的一种结构化描述,也是网页渲染的基础。
本文接下来就对网页DOMTree的创建过程进行详细分析。
网页的DOMTree的根节点是一个Document。
Document是依附在一个DOMWindow之上。
DOMWindow又是和一个Frame关联在一起的。
Document、DOMWindow和Frame都是WebKit里面的概念,其中Frame又是和Chromium的Content模块中的RenderFrame相对应的。
RenderFrame是和网页的FrameTree相关的一个概念。
关于网页的FrameTree,可以参考前面分析一文。
上面描述的各种对象的关系可以通过图1描述,如下所示:
从前面分析一文可以知道,有的RenderFrame只是一个Proxy,称为RenderFrameProxy。
RenderFrameProxy描述的是在另外一个Render进程中进行加载和渲染的网页。
这种网页在WebKit里面对应的Frame和DOMWindow分别称为RemoteFrame和RemoteDOMWindow。
由于RenderFrameProxy描述的网页不是在当前Render进程中加载和渲染,因此它是没有Document的。
相应地,RenderFrame描述的是在当前Render进程中进行加载和渲染的网页,它是具有Document的,并且这种网页在WebKit里面对应的Frame和DOMWindow分别称为LocalFrame和LocalDOMWindow。
从图1我们还可以看到,在RenderFrame和LocalFrame之间,以及RenderFrameProxy和RemoteFrame之间,分别存在一个WebLocalFrame和WebRemoteFrame。
WebLocalFrame和WebRemoteFrame是属于WebKitGlue层的概念。
从前面一文可以知道,WebKitGlue层的作用是将WebKit的对象类型转化为Chromium的对象类型,这样Chromium的Content层就可以用统一的、自有的方式管理所有的对象。
关于Chromium的层次划分和每一个层次的作用,可以参考前面一文。
除了根节点,也就是Document节点,DOMTree的每一个子结点对应的都是网页里面的一个HTML标签。
并不是所有的HTML标签都是需要渲染的,例如script标签就不需要进行渲染。
对于需要渲染的HTML标签,它们会关联有一个RenderObject。
这些RenderObject会形成一个RenderObjectTree,如图2所示:
为了便于执行绘制操作,具有相同坐标空间的RenderObject会绘制在同一个RenderLayer中。
这些RenderLayer又会形成一个RenderLayerTree。
绘制操作是由图形渲染引擎执行的。
对于图形渲染引擎来说,Layer是一个具有后端存储的概念。
在软件渲染模式中,Layer的后端存储实际上就是一个内存缓冲区。
在硬件渲染模式中,Layer的后端存储实际上就是一个FBO。
为了节约资源,WebKit不会为每一个RenderLayer都分配一个后端存储,而是会让某些RenderLayer共用其它的RenderLayer的后端存储。
那些具有自己的后端存储的RenderLayer,又称为GraphicsLayer。
这些GraphicsLayer又形成了一个GraphicsLayerTree。
RenderObjectTree、RenderLayerTree和GraphicsLayerTree都是和网页渲染相关概念,它们是从DOMTree发展而来的。
因此,在分析网页的渲染机制之前,有必要了解网页的DOMTree的创建过程。
DOMTree的创建发生在WebKit解析网页内容的过程中。
WebKit在解析网页内容的时候,会用到一个栈。
每当碰到一个HTML标签的起始Token,就会将其压入栈中,而当碰到该HTML标签的结束Token时,就会将其弹出栈。
在这些HTML标签的压栈和出栈过程中,就可以得到一棵DOMTree。
以图2所示的DOMTree片段为例,它对应的网页内容为:
[html]viewplaincopy在CODE上查看代码片派生到我的代码片
各个标签的压栈和出栈过程如图3所示:
接下来,我们就结合源码分析WebKit在解析网页内容的过程中创建DOMTree的过程。
从前面一文可以知道,Browser进程一边下载网页的内容,一边将下载回来的网页交给Render进程的Content模块。
Render进程的Content模块经过简单的处理之后,又会交给WebKit进行解析。
WebKit是从ResourceLoader类的成员函数didReceiveData开始接收Chromium的Content模块传递过来的网页内容的,因此我们就从这个函数开始分析WebKit解析网页内容的过程,也就是网页DOMTree的创建过程。
ResourceLoader类的成员函数didReceiveData的实现如下所示:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
voidResourceLoader:
:
didReceiveData(blink:
:
WebURLLoader*,constchar*data,intlength,intencodedDataLength)
{
......
m_resource->appendData(data,length);
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/fetch/ResourceLoader.cpp中。
ResourceLoader类的成员变量m_resource描述的是一个RawResource对象。
这个RawResource对象的创建过程可以参考前面一文。
ResourceLoader类的成员函数didReceiveData调用这个RawResource对象的成员函数appendData处理下载回来的网页内容。
RawResource类的成员函数appendData的实现如下所示:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
voidRawResource:
:
appendData(constchar*data,intlength)
{
Resource:
:
appendData(data,length);
ResourcePtr
ResourceClientWalker
while(RawResourceClient*c=w.next())
c->dataReceived(this,data,length);
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/fetch/RawResource.cpp中。
RawResource类的成员函数appendData主要是调用保存在成员变量m_clients中的每一个RawResourceClient对象的成员函数dataReceived,告知它们从Web服务器中下载回来了新的数据。
从前面一文可以知道,在RawResource类的成员变量m_clients中,保存有一个DocumentLoader对象。
这个DocumentLoader对象是从RawResourceClient类继承下来的,它负责创建和加载网页的文档对象。
接下来我们就继续分析它的成员函数dataReceived的实现,如下所示:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
voidDocumentLoader:
:
dataReceived(Resource*resource,constchar*data,intlength)
{
.....
commitData(data,length);
......
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/loader/DocumentLoader.cpp中。
DocumentLoader类的成员函数dataReceived主要是调用另外一个成员函数commitData处理从Web服务器下载回来的网页数据,后者的实现如下所示:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
voidDocumentLoader:
:
commitData(constchar*bytes,size_tlength)
{
ensureWriter(m_response.mimeType());
......
m_writer->addData(bytes,length);
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/loader/DocumentLoader.cpp中。
DocumentLoader类的成员函数commitData首先调用成员函数ensureWriter确定成员变量m_writer指向了一个DocumentWriter对象,因为接下来要调用这个DocumentWriter对象的成员函数addData对下载回来的网页数据进行解析。
接下来,我们首先分析DocumentLoader类的成员函数ensureWriter的实现,接下来再分析DocumentWriter类的成员函数addData的实现。
DocumentLoader类的成员函数ensureWriter的实现如下所示:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
voidDocumentLoader:
:
ensureWriter(constAtomicString&mimeType,constKURL&overridingURL)
{
if(m_writer)
return;
......
m_writer=createWriterFor(m_frame,0,url(),mimeType,encoding,false,false);
......
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/loader/DocumentLoader.cpp中。
DocumentLoader类的成员函数ensureWriter首先检查成员变量m_writer是否指向了一个DocumentWriter对象。
如果已经指向,那么就什么也不用做就直接返回。
否则的话,就会调用另外一个成员函数createWriterFor为当前正在加载的URL创建一个DocumentWriter对象,并且保存在成员变量m_writer中。
DocumentLoader类的成员函数createWriterFor的实现如下所示:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
PassRefPtrWillBeRawPtr
:
createWriterFor(LocalFrame*frame,constDocument*ownerDocument,constKURL&url,constAtomicString&mimeType,constAtomicString&encoding,booluserChosen,booldispatch)
{
......
//Insomerarecases,we'llre-usedaLocalDOMWindowforanewDocument.Forexample,
//whenascriptcallswindow.open("..."),thebrowsergivesJavaScriptawindow
//synchronouslybutkicksofftheloadinthewindowasynchronously.Websites
//expectthatmodificationsthattheymaketothewindowobjectsynchronously
//won'tbeblownawaywhenthenetworkloadcommits.Tomakethathappen,we
//"securelytransition"theexistingLocalDOMWindowtotheDocumentthatresultsfrom
//thenetworkload.SeealsoSecurityContext:
:
isSecureTransitionTo.
boolshouldReuseDefaultView=frame->loader().stateMachine()->isDisplayingInitialEmptyDocument()&&frame->document()->isSecureTransitionTo(url);
......
if(!
shouldReuseDefaultView)
frame->setDOMWindow(LocalDOMWindow:
:
create(*frame));
RefPtrWillBeRawPtr
......
returnDocumentWriter:
:
create(document.get(),mimeType,encoding,userChosen);
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/loader/DocumentLoader.cpp中。
从前面的调用过程可以知道,参数frame描述的LocalFrame对象来自于DocumentLoader类的成员变量m_frame,这个LocalFrame对象描述的是一个在当前Render进程中进行加载的网页。
如果当前正在加载的网页是通过JavaScript接口window.open打开的,那么参数frame描述的LocalFrame对象已经关联有一个默认的DOMWindow。
在符合安全规则的情况下,这个默认的DOMWindow将会被使用。
如果不符合安全规则,或者当前加载的网页不是通过JavaScript接口window.open打开的,那么就需要为参数frame描述的LocalFrame对象创建一个新的DOMWindow。
这是通过调用LocalDOMWindow类的静态成员函数create创建的,如下所示:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
namespaceWebCore{
......
classLocalDOMWindowFINAL:
publicRefCountedWillBeRefCountedGarbageCollected
......
staticPassRefPtrWillBeRawPtr
{
returnadoptRefWillBeRefCountedGarbageCollected(newLocalDOMWindow(frame));
}
......
};
......
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/frame/LocalDOMWindow.h中。
LocalDOMWindow类的静态成员函数create为参数frame指向的一个LocalFrame对象创建的是一个类型为LocalDOMWindow的DOMWindow,这是因为参数frame指向的LocalFrame对象描述的是一个在当前Render进程加载的网页。
回到DocumentLoader类的成员函数createWriterFor中,它调用LocalDOMWindow类的静态成员函数create创建了一个LocalDOMWindow对象之后,会将这个LocalDOMWindow对象设置给参数frame描述的LocalFrame对象。
这是通过调用LocalFrame类的成员函数setDOMWindow实现的。
LocalFrame类的成员函数setDOMWindow的实现如下所示:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
voidLocalFrame:
:
setDOMWindow(PassRefPtrWillBeRawPtr
{
......
Frame:
:
setDOMWindow(domWindow);
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/frame/LocalFrame.cpp中。
LocalFrame类的成员函数setDOMWindow会将参数domWindow描述的一个LocalDOMWindow对象交给父类Frame处理,这是通过调用父类Frame的成员函数setDOMWindow实现的。
Frame类的成员函数setDOMWindow的实现如下所示:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
voidFrame:
:
setDOMWindow(PassRefPtrWillBeRawPtr
{
......
m_domWindow=domWindow;
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/frame/Frame.cpp中。
Frame类的成员函数setDOMWindow将参数domWindow描述的一个LocalDOMWindow对象保存在成员变量m_domWindow中。
以后就可以通过调用Frame类的成员函数domWindow获得这个LocalDOMWindow对象,如下所示:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
inlineLocalDOMWindow*Frame:
:
domWindow()const
{
returnm_domWindow.get();
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/frame/Frame.h中。
这一步执行完成之后,WebKit就为一个类型为LocaFrame的Frame创建了一个类型为LocalDOMWindow的DOMWindow,正如图1所示。
回到DocumentLoader类的成员函数createWriterFor中,接下来它会继续为上面创建的类型为LocalDOMWindow的DOMWindow创建一个Document。
这是通过调用LocalDOMWindow类的成员函数installNewDocument实现的,如下所示:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
PassRefPtrWillBeRawPtr
:
installNewDocument(constString&mimeType,constDocumentInit&init,boolforceXHTML)
{
......
m_document=createDocument(mimeType,init,forceXHTML);
......
m_document->attach();
......
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp中。
LocalDOMWindow类的成员函数installNewDocument首先调用另外一个成员函数createDocument创建一个HTMLDocument对象,并且保存在成员变量m_document中,接下来又调用这个HTMLDocument对象的成员函数attach为其创建一个RenderView。
这个RenderView即为图2所示的RenderObjectTree的根节点。
接下来我们首先分析LocalDOMWindow类的成员函数createDocument的实现,接着再分析HTMLDocument类的成员函数attach的实现。
LocalDOMWindow类的成员函数createDo
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Chromium网页DOM Tree创建过程分析 Chromium 网页 DOM Tree 创建 过程 分析