Chromium的Plugin进程启动过程分析.docx

上传人:b****6 文档编号:7956944 上传时间:2023-01-27 格式:DOCX 页数:41 大小:119.22KB
下载 相关 举报
Chromium的Plugin进程启动过程分析.docx_第1页
第1页 / 共41页
Chromium的Plugin进程启动过程分析.docx_第2页
第2页 / 共41页
Chromium的Plugin进程启动过程分析.docx_第3页
第3页 / 共41页
Chromium的Plugin进程启动过程分析.docx_第4页
第4页 / 共41页
Chromium的Plugin进程启动过程分析.docx_第5页
第5页 / 共41页
点击查看更多>>
下载资源
资源描述

Chromium的Plugin进程启动过程分析.docx

《Chromium的Plugin进程启动过程分析.docx》由会员分享,可在线阅读,更多相关《Chromium的Plugin进程启动过程分析.docx(41页珍藏版)》请在冰豆网上搜索。

Chromium的Plugin进程启动过程分析.docx

Chromium的Plugin进程启动过程分析

Chromium的Plugin进程启动过程分析

前面我们分析了Chromium的Render进程和GPU进程的启动过程,它们都是由Browser进程启动的。

在Chromium中,还有一类进程是由Browser进程启动的,它们就是Plugin进程。

顾名思义,Plugin进程是用来运行浏览器插件的。

浏览器插件的作用是扩展网页功能,它们由第三方开发,安全性和稳定性都无法得到保证,因此运行在独立的进程中。

本文接下来就详细分析Plugin进程的启动过程。

在Chromium中,还有一种组件比较容易和浏览器插件混淆,它们称为浏览器扩展(Extension)。

浏览器扩展工作在浏览器层面上,用来扩展浏览器的功能,例如可以在浏览器的工具栏上增加一个按钮,并且通过浏览器提供的API实现某种功能,例如翻墙。

浏览器插件工作在网页层面上,用来扩展网页的功能,例如在网页上显示一个PDF文档或者播放Flash视频。

本文要分析的是浏览器插件。

在Chromium中,浏览器插件又分为两类,一类称为NPAPI插件,另一类称为PPAPI插件。

NPAPI的全称是NetscapePluginAPI,就是运行在NetscapeNavigator浏览器上的一种插件,也就是由Netscape定义一套API接口,插件开发商通过调用这些API接口来扩展网页的功能。

NetscapeNavigator浏览器虽然早已离我们远去,但是NPAPI插件还顽强地活着,并且成为了一种几乎所有浏览器都支持的跨平台插件标准。

因此,Chromium也对NPAPI插件进行了支持,目的是为了可以运行数以万计已经存在的NPAPI插件。

但是由于NPAPI插件定义的API接口太古老了,也太难用了,崩溃问题和安全问题也很多,无法适应现代浏览器的发展,因此Chromium搞了另外一套API接口,称为PPAPI接口,全称是PepperPluginAPI。

基于PPAPI实现的插件就称为PPAPI插件。

Chromium从2014年1月,逐渐放弃对NPAPI插件的支持,因此本文接下来要分析的是PPAPI插件。

PPAPI插件使用了一种称为NativeClient(NaCl)的技术来解决安全性问题。

在NativeClient技术中,我们可以使用熟悉的Native语言C/C++开发插件核心模块,以满足效率要求。

这些C/C++模块通过IPC可以与网页中的JS进行通信,同时它们还可以通过PPAPI使用浏览器提供的功能,例如可以通过PPB_OpenGLES2接口使用浏览器提供的OpenGL渲染能力。

为了保证安全性,NativeClient在工具链层面上限制C/C++模块能够调用的本地OS提供的API接口。

也就是说,PPAPI插件的C/C++模块要通过NativeClient提供的编译工具进行编译,而这些编译工具会检查PPAPI插件的C/C++模块调用的API,并且禁止非法API调用。

这样实际上就给PPAPI插件加上了两层安全保护。

第一层安全保护体现在运行时,将PPAPI插件运行在一个受限进程中,即运行在一个沙箱中。

第二层安全保护体现在编译时,禁止Native代码调用非法API。

PPAPI插件有三种运行模式。

第一种模式是运行在一个受限的独立进程中,即运行在沙箱中。

第二种模式运行在一个非受限的独立进程中。

第三种模式直接运行在Render进程中。

一个PPAPI插件运行在哪一种模式取决于浏览器内部实现和浏览器命令行参数。

浏览器内部实现会对某些内部PPAPI的运行模式进行HardCode,例如PDF插件。

这一点可以参考ChromeContentClient类的成员函数AddPepperPlugins(定义在文件external/chromium_org/chrome/common/chrome_content_client.cc中)。

此外,如果浏览器命令行参数指定了switches:

:

kPpapiInProcess选项,则其余安装的PPAPI插件将运行在第三种模式中,否则的话,就运行在第一种模式中。

这一点可以参考函数ComputePluginsFromCommandLine(定义在文件external/chromium_org/content/common/pepper_plugin_list.cc中)。

本文主要关注的是PPAPI插件进程的启动过程。

通过这个启动过程,我们可以了解PPAPI插件进程与Browser进程和Render进程的关系,如下所示:

Render进程在解析网页时,如果遇到一个embed标签,那么就会创建一个HTMLPlugInElement对象来描述该标签,并且调用该HTMLPlugInElement对象的成员函数loadPlugin加载对应的PPAPI插件。

但是Render进程没有启动PPAPI插件进程的权限,因此它就会通过之前与Browser进程建立的IPC通道向Browser进程发出一个启动PPAPI插件进程的请求。

Browser进程接收到启动PPAPI插件进程的请求之后,就会启动一个PPAPI插件进程,并且创建一个PpapiPluginProcessHost对象描述该PPAPI插件进程。

PPAPI插件进程启动起来之后,会在内部创建一个ChildProcess对象,用来与Browser进程中的PpapiPluginProcessHost对象建立一个IPC通道。

有了这个IPC通道之后,Browser进程请求PPAPI插件进程创建另外一个UINXSocket。

该UNIXSocket的Server端文件描述符保留在PPAPI进程中,Client端文件描述符则返回给Render进程。

基于这个UNIXSocket的Server端和Client端文件描述符,PPAPI插件进程和Render进程将分别创建一个HostDispatcher对象和一个PluginDispatcher对象,构成一个Plugin通道。

以后PPAPI插件进程和Render进程就通过该Plugin通道进行通信。

接下来,我们就从HTMLPlugInElement类的成员函数loadPlugin开始,分析PPAPI插件进程的启动过程,如下所示:

[cpp]viewplaincopy

boolHTMLPlugInElement:

:

loadPlugin(constKURL&url,constString&mimeType,constVector¶mNames,constVector¶mValues,booluseFallback,boolrequireRenderer)

{

LocalFrame*frame=document().frame();

......

RefPtrwidget=m_persistedPluginWidget;

if(!

widget){

......

widget=frame->loader().client()->createPlugin(this,url,paramNames,paramValues,mimeType,loadManually,policy);

}

......

returntrue;

}

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

HTMLPlugInElement类的成员变量m_persistedPluginWidget的值等于NULL的时候,说明当前正在处理的HTMLPlugInElement对象描述的插件实例还没有创建,或者需要重新创建。

在这种情况下,HTMLPlugInElement类的成员函数loadPlugin就会首先获得当前网页的Document对象,然后通过该Document对象获得一个LocalFrame对象,这个LocalFrame对象描述的是当前网页的页框。

每一个LocalFrame对象都有一个关联的FrameLoader对象,这个FrameLoader对象用来加载当前网页的页框。

每一个FrameLoader对象又关联有一个FrameLoaderClientImpl对象,通过这个FrameLoaderClientImpl对象,WebKit可以用来请求执行平台相关的操作,例如,HTMLPlugInElement类的成员函数loadPlugin就通过调用它的成员函数createPlugin创建一个插件实例。

FrameLoaderClientImpl类的成员函数createPlugin的实现如下所示:

[cpp]viewplaincopy

PassRefPtrFrameLoaderClientImpl:

:

createPlugin(

HTMLPlugInElement*element,

constKURL&url,

constVector¶mNames,

constVector¶mValues,

constString&mimeType,

boolloadManually,

DetachedPluginPolicypolicy)

{

......

WebPlugin*webPlugin=m_webFrame->client()->createPlugin(m_webFrame,params);

......

//ThecontainertakesownershipoftheWebPlugin.

RefPtrcontainer=

WebPluginContainerImpl:

:

create(element,webPlugin);

if(!

webPlugin->initialize(container.get()))

returnnullptr;

......

returncontainer;

}

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

FrameLoaderClientImpl类的成员变量m_webFrame指向的是一个WebLocalFrameImpl对象,该WebLocalFrameImpl对象用来描述当前处理的网页内容是在当前进程进行渲染的,并且通过该WebLocalFrameImpl对象的成员函数client可以获得一个WebFrameClient对象,该WebFrameClient对象是用WebKit用来与上层使用者,即Chromium进行交互的,例如WebKit就通过调用它的成员函数createPlugin创建一个插件实例。

创建出来的插件实例在WebKit中用一个WebPlugin对象描述,这个WebPlugin对象最后封装在一个WebPluginContainerImpl对象中返回给调用者。

创建出来的WebPlugin对象在返回给调用者之前,会先调用其成员函数initailize进行初始化。

对于NPAPI插件来说,初始化工作包括请求Browser进程启动一个插件进程,但是对于PPAPI插件来说,插件进程是在上述的插件实例创建过程中请求Browser进程启动的,接下来我们就会到这一点。

由于NPAPI插件进程和PPAPI插件进程的启动过程类似,因此我们只关注PPAPI插件进程的启动过程。

Chromium将上述WebFrameClient对象指定为一个RenderFrameImpl对象,这个RenderFrameImpl对象是Chromium用来描述当前正在处理的一个网页的,它的成员函数createPlugin的实现如下所示:

[cpp]viewplaincopy

blink:

:

WebPlugin*RenderFrameImpl:

:

createPlugin(

blink:

:

WebLocalFrame*frame,

constblink:

:

WebPluginParams¶ms){

DCHECK_EQ(frame_,frame);

blink:

:

WebPlugin*plugin=NULL;

if(GetContentClient()->renderer()->OverrideCreatePlugin(

this,frame,params,&plugin)){

returnplugin;

}

if(base:

:

UTF16ToASCII(params.mimeType)==kBrowserPluginMimeType){

returnrender_view_->GetBrowserPluginManager()->CreateBrowserPlugin(

render_view_.get(),frame,false);

}

#ifdefined(ENABLE_PLUGINS)

WebPluginInfoinfo;

std:

:

stringmime_type;

boolfound=false;

Send(newFrameHostMsg_GetPluginInfo(

routing_id_,params.url,frame->top()->document().url(),

params.mimeType.utf8(),&found,&info,&mime_type));

if(!

found)

returnNULL;

if(info.type==content:

:

WebPluginInfo:

:

PLUGIN_TYPE_BROWSER_PLUGIN){

returnrender_view_->GetBrowserPluginManager()->CreateBrowserPlugin(

render_view_.get(),frame,true);

}

WebPluginParamsparams_to_use=params;

params_to_use.mimeType=WebString:

:

fromUTF8(mime_type);

returnCreatePlugin(frame,info,params_to_use);

#else

returnNULL;

#endif//defined(ENABLE_PLUGINS)

}

这个函数定义在文件external/chromium_org/content/renderer/render_frame_impl.cc中。

Chromium是多层架构的,从上到下大概分为浏览器层、Content层和WebKit层。

其中,WebKit层用来解析网页,Content层位于WebKit层之上,并且提供多进程架构。

这样我们就可以基于Content层,实现一个浏览器。

例如,Chrome就是基于Chromium的Content层提供的API实现的。

这意味着我们也可以基于Chromium的Content层实现一个与Chrome类似的浏览器。

Chromium为了能够让浏览层定制一些Content层的行为,允许浏览器层设置一个ContentClient到Content层来。

Chromium在执行某些操作时,就会首先通过函数GetContentClient获得上述ContentClient,然后再通过它询问浏览层是否需要接管该操作。

例如,在我们这个场景中,RenderFrameImpl类的成员函数createPlugin会先询问浏览器层是否需要接管创建插件实例的操作。

如果浏览器层需要接管,那么它就负责根据指定的参数创建一个相应的插件实现,这时候RenderFrameImpl类的成员函数createPlugin就什么也不用做。

浏览器层设置到Content层的ContentClient包含有两个部分,一部分称为Browser端,另一部分称为Renderer端。

其中,Browser端运行在Chromium的Browser进程中,负责接管Chromium的Browser进程的某些操作,而Renderer端运行在Chromium的Render进程中,负责接管Chromium的Render进程的某些操作。

从这里我们可以看到,RenderFrameImpl类的成员函数createPlugin是通过调用浏览器层设置到Content层的ContentClient的Renderer端的成员函数OverrideCreatePlugin来询问浏览层是否需要接管创建插件实例的操作的。

假设浏览层不接管创建插件实例的操作,那么RenderFrameImpl类的成员函数createPlugin接下来判断embed标签的type属性值是否等于kBrowserPluginMimeType,即“application/browser-plugin”。

如果等于的话,那么就意味着要创建一个BrowserPlugin,这是通过调用一个BrowserPluginManager的成员函数CreateBrowserPlugin创建的。

BrowserPlugin的作用类似于HTML中的iframe,是用来在当前网页中内嵌另外一个网页的,更详细的信息可以参考这里:

注意,BrowserPlugin是由Chromium提供实现支持的,而不是某一个Plugin。

假设embed标签的type属性值不等于kBrowserPluginMimeType,那么RenderFrameImpl类的成员函数createPlugin接下来向Browser进程发送一个类型为FrameHostMsg_GetPluginInfo的IPC消息,用来获取要创建的插件实例的更详细的信息,实际上就是通过embed标签的type属性值在已安装的插件列表找到一个对应的插件。

如果没有找到与embed标签的type属性值对应的插件,那么RenderFrameImpl类的成员函数createPlugin就什么也不做就返回了。

另一方面,如果找到的插件的类型为content:

:

WebPluginInfo:

:

PLUGIN_TYPE_BROWSER_PLUGIN,那么同样意味要创建一个BrowserPlugin,因此这时候也会调用BrowserPluginManager的成员函数CreateBrowserPlugin创建一个BrowserPlugin。

假设找到了一个对应的插件,并且该插件的类型不是content:

:

WebPluginInfo:

:

PLUGIN_TYPE_BROWSER_PLUGIN,那么RenderFrameImpl类的成员函数createPlugin接下来就会调用另外一个成员函数CreatePlugin创建一个插件实现,如下所示:

[cpp]viewplaincopy

blink:

:

WebPlugin*RenderFrameImpl:

:

CreatePlugin(

blink:

:

WebFrame*frame,

constWebPluginInfo&info,

constblink:

:

WebPluginParams¶ms){

DCHECK_EQ(frame_,frame);

#ifdefined(ENABLE_PLUGINS)

boolpepper_plugin_was_registered=false;

scoped_refptrpepper_module(PluginModule:

:

Create(

this,info,&pepper_plugin_was_registered));

if(pepper_plugin_was_registered){

if(pepper_module.get()){

returnnewPepperWebPluginImpl(pepper_module.get(),params,this);

}

}

......

//TODO(jam):

changetotakeRenderFrame.

returnnewWebPluginImpl(frame,params,info.path,render_view_,this);

#endif

#else

returnNULL;

#endif

}

这个函数定义在文件external/chromium_org/content/renderer/render_frame_impl.cc中。

RenderFrameImpl类的成员函数CreatePlugin首先是调用PluginModule类的成员函数Create查询要创建的插件是否是一个PPAPI插件。

如果是的话,那么PluginModule类的成员函数Create会将输出参数pepper_plugin_was_registered的值设置为true,并且会返回一个PluginModule对象,用来描述PPAPI插件所在的模块,最后这个PluginModule对象将会被封装在一个PepperWebPluginImpl对象,并且返回给调用者。

这意味着PPAPI插件是通过PepperWebPluginImpl类来描述的。

另一方面,如果PluginModule类的成员函数Create将输出参数pepper_plugin_was_registered的值设置为false,那么就意味着要创建的是一个NPAPI插件。

NPAPI插件通过WebPluginImpl类来描述,因此在这种情况下,RenderFrameImpl类的成员函数CreatePlugin就创建一个WebPluginImpl对象返回给调用者。

接下来我们继续分析PluginModule类的成员函数Create的实现,如下所示:

[cpp]viewplaincopy

scoped_refptrPluginModule:

:

Create(

RenderFrameImpl*render_frame,

constWebPluginInfo&webplugin_info,

bool*pepper_plugin_was_registered){

*pepper_plugin_was_registered=true;

//Seeifamodulehasalreadybeenloadedforthisplugin.

base:

:

FilePathpath(webplugin_info.path);

scoped_refptrmodule=

PepperPluginRegistry:

:

GetInstance()->GetLiveModule(path);

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 工作范文 > 行政公文

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1