Chromium在解析网页时,每遇到一个
Plugin Instance在运行的过程中,需要调用Chromium提供的API。这些API是实现在Render进程中的。与此同时,Render进程也需要主动与Plugin Instance通信,例如当网页的
Plugin Instance的创建是由运行在Render进程中的WebKit发起的。WebKit又是通过请求Chromium的Content层创建Plugin Instance的。Content层在创建Plugin Instance之前,先会检查它对应的Plugin Module是否已经加载。如果还没有加载,那么就会先加载。这个加载过程可以参考前面Chromium插件(Plugin)模块(Module)加载过程分析一文。接下来,Content层又会创建一个Plugin Instance Proxy,即一个PepperPluginInstanceImpl对象,然后再通过个PepperPluginInstanceImpl对象请求在相应的Plugin进程中创建一个Plugin Instance。这个过程如图1所示:
图1 Plugin Instance的创建过程
在PepperPluginInstanceImpl对象的创建这程中,会初始化一系列的接口。以后PepperPluginInstanceImpl对象就是通这些接口与运行在Plugin进程的Plugin Instance通信的。其中的一个接口在创建Plugin Instance的过程中也会用到。这个接口称为PPP_INSTANCE_INTERFACE_1_1,它提供了一个DidCreate函数。通过这个函数,PepperPluginInstanceImpl对象就可以请求在指定的Plugin进程中创建一个Plugin Instance。
接口PPP_INSTANCE_INTERFACE_1_1提供的函数DidCreate会向Plugin进程发送一个类型为PpapiMsg_PPPInstance_DidCreate的IPC消息。这个IPC消息携带了一个参数API_ID_INSTANCE,表示该消息是分发给一个PPP_Instance_Proxy对象处理。这个PPP_Instance_Proxy对象又会在Plugin进程中获得一个PPP_INSTANCE_INTERFACE_1_1接口,并且调用该接品提供的函数DidCreate。后者在执行的过程中,就会创建一个pp::Instance对象。这个pp::Instance对象就是用来在Plugin对象中描述一个Plugin Instance的。
从前面Chromium的GPU进程启动过程分析和Chromium插件(Plugin)模块(Module)加载过程分析这两篇文章可以知道,WebKit请求Chromium的Content层为
PepperWebPluginImpl类的成员函数initialize的实现如下所示:
bool PepperWebPluginImpl::initialize(WebPluginContainer* container) {
// The plugin delegate may have gone away.
instance_ = init_data_->module->CreateInstance(
init_data_->render_frame, container, init_data_->url);
......
bool success = instance_->Initialize(
init_data_->arg_names, init_data_->arg_values, full_frame_);
......
return true;
}
这个函数定义在文件external/chromium_org/content/renderer/pepper/pepper_webplugin_impl.cc中。
PepperWebPluginImpl类的成员变量init_data_指向的是一个InitData对象。这个InitData对象的成员变量module指向的是一个PluginModule对象。这个PluginModule对象用来在Render进程中描述一个Plugin Module,它的创建过程可以参考前面Chromium的GPU进程启动过程分析一文。
有了上述PluginModule对象之后,PepperWebPluginImpl类的成员函数initialize就可以调用它的成员函数CreateInstance创建一个Plugin Instance Proxy,即一个PepperPluginInstanceImpl对象,并且接下来会调用这个PepperPluginInstanceImpl对象的成员函数Initialize执行进行初始化工作。在初始化的过程中,就会请求Plugin进程创建一个Plugin Instance。
接下来我们先分析Plugin Instance Proxy的创建这程,也就是PluginModule类的成员函数CreateInstance的实现,如下所示:
PepperPluginInstanceImpl* PluginModule::CreateInstance(
RenderFrameImpl* render_frame,
blink::WebPluginContainer* container,
const GURL& plugin_url) {
PepperPluginInstanceImpl* instance = PepperPluginInstanceImpl::Create(
render_frame, this, container, plugin_url);
......
return instance;
}
这个函数定义在文件external/chromium_org/content/renderer/pepper/plugin_module.cc中。
PluginModule类的成员函数CreateInstance通过调用PepperPluginInstanceImpl类的静态成员函数Create创建一个PepperPluginInstanceImpl对象 ,如下所示:
PepperPluginInstanceImpl* PepperPluginInstanceImpl::Create(
RenderFrameImpl* render_frame,
PluginModule* module,
WebPluginContainer* container,
const GURL& plugin_url) {
base::Callback<const void*(const char*)> get_plugin_interface_func =
base::Bind(&PluginModule::GetPluginInterface, module);
PPP_Instance_Combined* ppp_instance_combined =
PPP_Instance_Combined::Create(get_plugin_interface_func);
if (!ppp_instance_combined)
return NULL;
return new PepperPluginInstanceImpl(
render_frame, module, ppp_instance_combined, container, plugin_url);
}
这个函数定义在文件external/chromium_org/content/renderer/pepper/pepper_plugin_instance_impl.cc中。
PepperPluginInstanceImpl类的静态成员函数Create首先是将PluginModule类的成员函数GetPluginInterface封装在一个Callback中,然后再将这个Callback传递给PPP_Instance_Combined类的静态成员函数Create,用来创建一个PPP_Instance_Combined对象。有了这个PPP_Instance_Combined对象之后,就可以创建一个PepperPluginInstanceImpl对象了。
接下来,我们首先分析上述PPP_Instance_Combined对象的创建过程,也就是PPP_Instance_Combined类的静态成员函数Create的实现,如下所示:
PPP_Instance_Combined* PPP_Instance_Combined::Create(
base::Callback<const void*(const char*)> get_interface_func) {
// Try 1.1.
const void* ppp_instance = get_interface_func.Run(PPP_INSTANCE_INTERFACE_1_1);
if (ppp_instance) {
const PPP_Instance_1_1* ppp_instance_1_1 =
static_cast<const PPP_Instance_1_1*>(ppp_instance);
return new PPP_Instance_Combined(*ppp_instance_1_1);
}
// Failing that, try 1.0.
ppp_instance = get_interface_func.Run(PPP_INSTANCE_INTERFACE_1_0);
if (ppp_instance) {
const PPP_Instance_1_0* ppp_instance_1_0 =
static_cast<const PPP_Instance_1_0*>(ppp_instance);
return new PPP_Instance_Combined(*ppp_instance_1_0);
}
// No supported PPP_Instance version found.
return NULL;
}
这个函数定义在文件external/chromium_org/ppapi/shared_impl/ppp_instance_combined.cc中。
PPP_Instance_Combined类的静态成员函数Create首先尝试通过参数get_interface_func描述的Callback获得一个1.1版本的PPP_INSTANCE_INTERFACE接口。如果能成功获得该接口,那么就使用它来创建一个PPP_Instance_Combined对象返回给调用者。
如果不能成功获得1.1版本的PPP_INSTANCE_INTERFACE接口,那么PPP_Instance_Combined类的静态成员函数Create接下来会尝试获得1.0版本的PPP_INSTANCE_INTERFACE接口。如果能成功获得该接口,那么同样会使用它来创建一个PPP_Instance_Combined对象返回给调用者。
我们假设1.1版本的PPP_INSTANCE_INTERFACE接口能够成功获取,接下来我们就分析它的获得过程。从前面的分析可以知道,参数get_interface_func描述的Callback描述的实际上是PluginModule类的成员函数GetPluginInterface,因此,1.1版本的PPP_INSTANCE_INTERFACE接口实际上是通过调用PluginModule类的成员函数GetPluginInterface获得的,如下所示:
const void* PluginModule::GetPluginInterface(const char* name) const {
if (host_dispatcher_wrapper_)
return host_dispatcher_wrapper_->GetProxiedInterface(name);
......
}
这个函数定义在文件external/chromium_org/content/renderer/pepper/plugin_module.cc中。
PluginModule类的成员变量host_dispatcher_wrapper_指向的是一个HostDispatcherWrapper对象。这个HostDispatcherWrapper对象是在Plugin Module加载的过程中创建的,它内部封装了一个HostDispatcher对象,通过这个HostDispatcher对象可以与Plugin Module所加载在的Plugin进程通信。
有了上述HostDispatcherWrapper对象之后,PluginModule类的成员函数GetPluginInterface就可以调用它的成员函数GetProxiedInterface获得版本为PPP_INSTANCE_INTERFACE接口,即PPP_INSTANCE_INTERFACE_1_1接口,如下所示:
const void* HostDispatcherWrapper::GetProxiedInterface(const char* name) {
return dispatcher_->GetProxiedInterface(name);
}
这个函数定义在文件external/chromium_org/content/renderer/pepper/host_dispatcher_wrapper.cc中。
HostDispatcherWrapper类的成员变量dispatcher_指向的就是一个HostDispacther对象。HostDispatcherWrapper类的成员函数GetProxiedInterface通过调用这个HostDispacther对象的成员函数GetProxiedInterface获得PPP_INSTANCE_INTERFACE_1_1接口,如下所示:
const void* HostDispatcher::GetProxiedInterface(const std::string& iface_name) {
const void* proxied_interface =
InterfaceList::GetInstance()->GetInterfaceForPPP(iface_name);
if (!proxied_interface)
return NULL; // Don't have a proxy for this interface, don't query further.
PluginSupportedMap::iterator iter(plugin_supported_.find(iface_name));
if (iter == plugin_supported_.end()) {
// Need to query. Cache the result so we only do this once.
bool supported = false;
bool previous_reentrancy_value = allow_plugin_reentrancy_;
allow_plugin_reentrancy_ = true;
Send(new PpapiMsg_SupportsInterface(iface_name, &supported));
allow_plugin_reentrancy_ = previous_reentrancy_value;
std::pair<PluginSupportedMap::iterator, bool> iter_success_pair;
iter_success_pair = plugin_supported_.insert(
PluginSupportedMap::value_type(iface_name, supported));
iter = iter_success_pair.first;
}
if (iter->second)
return proxied_interface;
return NULL;
}
这个函数定义在文件external/chromium_org/ppapi/proxy/host_dispatcher.cc中。
从前面Chromium插件(Plugin)模块(Module)加载过程分析一文可以知道,Plugin进程中存在一个InterfaceList单例对象。这个InterfaceList单例对象在创建的过程中,会初始化一系列的接口。这些接口是提供给Plugin Instance调用的,以便Plugin Instance可以与它们在Render进程中的Proxy进行通信。
同样,在Render进程中,也存在一个InterfaceList单例对象。这个InterfaceList单例对象在创建的过程中,也会初始化一系列的接口,不过这些接口是提供给Plugin Instance Proxy调用的,以便Plugin Instance Proxy可以与它们在Plugin进程中的Plugin Instance进行通信。
HostDispacther对象的成员函数GetProxiedInterface所做的事情就是检查Render进程中的InterfaceList单例对象是否提供了PPP_INSTANCE_INTERFACE_1_1接口。如果有提供,并且Plugin进程支持该接口,那么就会将它返回给调用者。注意,检查Plugin进程是否支持某个接口,是通过向它发送一个类型为PpapiMsg_SupportsInterface的IPC消息实现的。这里我们假设Plugin进程支持PPP_INSTANCE_INTERFACE_1_1接口。
接下来我们先分析Render进程中的InterfaceList单例对象在创建过程中提供的三个Plugin Instance Proxy可以调用的接口,它们在接下来一篇文章分析Plugin Instance的3D渲染过程时将会使用到,如下所示:
InterfaceList::InterfaceList() {
memset(id_to_factory_, 0, sizeof(id_to_factory_));
// Register the API factories for each of the API types. This calls AddProxy
// for each InterfaceProxy type we support.
#define PROXIED_API(api_name) \
AddProxy(PROXY_API_ID(api_name), &PROXY_FACTORY_NAME(api_name));
// Register each proxied interface by calling AddPPB for each supported
// interface. Set current_required_permission to the appropriate value for
// the value you want expanded by this macro.
#define PROXIED_IFACE(iface_str, iface_struct) \
AddPPB(iface_str, \
INTERFACE_THUNK_NAME(iface_struct)(), \
current_required_permission);
{
Permission current_required_permission = PERMISSION_NONE;
......
#include "ppapi/thunk/interfaces_ppb_public_stable.h"
}
......
AddPPP(PPP_INSTANCE_INTERFACE_1_1,
PPP_Instance_Proxy::GetInstanceInterface());
......
}
这个函数定义在文件external/chromium_org/ppapi/proxy/interface_list.cc。
这三个接口分别是API_ID_PPB_INSTANCE、API_ID_PPB_GRAPHICS_3D和PPP_INSTANCE_INTERFACE_1_1。其中,API_ID_PPB_INSTANCE和API_ID_PPB_GRAPHICS_3D这两个接口是通过include文件interfaces_ppb_public_stable.h进行定义的,如下所示:
PROXIED_API(PPB_Graphics3D)
......
PROXIED_API(PPB_Instance)
这两个接口定义在文件external/chromium_org/ppapi/thunk/interfaces_ppb_public_stable.h中。
其中,接口API_ID_PPB_INSTANCE由语句PROXIED_API(PPB_Instance)定义。我们在前面Chromium插件(Plugin)模块(Module)加载过程分析一文已经分析过这个接口的定义了,因此这里不再复述。实现这个接口的是一个模板函数ProxyFactory
接口API_ID_PPB_GRAPHICS_3D由语句PROXIED_API(PPB_Graphics3D)定义。它的定义过程与接口API_ID_PPB_INSTANCE是一样的,因此,我们容易知道,实现这个接口的是另外一个模板函数ProxyFactory
回到InterfaceList类的构造函数中,第三个接口PPP_INSTANCE_INTERFACE_1_1是由一个PPP_Instance_1_1对象实现的。这个PPP_Instance_1_1对象可以通过调用PPP_Instance_Proxy类的静态成员函数GetInstanceInterface获得,如下所示:
static const PPP_Instance_1_1 instance_interface = {
&DidCreate,
&DidDestroy,
&DidChangeView,
&DidChangeFocus,
&HandleDocumentLoad
};
......
const PPP_Instance* PPP_Instance_Proxy::GetInstanceInterface() {
return &instance_interface;
}
这个函数定义在文件external/chromium_org/ppapi/proxy/ppp_instance_proxy.cc中。
从这里我们就可以看到,接口PPP_INSTANCE_INTERFACE_1_1提供给Plugin Instance Proxy使用的函数,例如函数DidCreate,是用来请求Plugin进程创建一个Plugin Instance的,接下来我们就会看到这个函数的使用过程。
分析到这里,我们就可以知道,HostDispacther类的成员函数GetProxiedInterface通过调用Render进程中的InterfaceList单例对象的成员函数GetInterfaceForPPP是可以获得PPP_INSTANCE_INTERFACE_1_1接口的。这个接口会沿着调用过程返回给PPP_Instance_Combined类的静态成员函数Create。
PPP_Instance_Combined类的静态成员函数Create获得了PPP_INSTANCE_INTERFACE_1_1接口之后,就会使用它来创建一个PPP_Instance_Combined对象,如下所示:
PPP_Instance_Combined::PPP_Instance_Combined(
const PPP_Instance_1_1& instance_if)
: instance_1_1_(instance_if), did_change_view_1_0_(NULL) {}
这个函数定义在文件external/chromium_org/ppapi/shared_impl/ppp_instance_combined.cc中。
参数instance_if描述的就是前面获得的PPP_INSTANCE_INTERFACE_1_1接口。这个接口实际上是一个PPP_Instance_1_1对象。这个PPP_Instance_1_1对象将会保存在PPP_Instance_Combined类的成员变量instance_1_1_中。
这一步执行完成后,我们就获得了一个PPP_Instance_Combined对象。这个PPP_Instance_Combined对象将会返回给前面分析的PepperPluginInstanceImpl类的静态成员函数Create,后者将会使用前者创建一个PepperPluginInstanceImpl对象。这个PepperPluginInstanceImpl对象的创建过程如下所示:
PepperPluginInstanceImpl::PepperPluginInstanceImpl(
RenderFrameImpl* render_frame,
PluginModule* module,
ppapi::PPP_Instance_Combined* instance_interface,
WebPluginContainer* container,
const GURL& plugin_url)
: ......,
module_(module),
instance_interface_(instance_interface),
...... {
......
RendererPpapiHostImpl* host_impl = module_->renderer_ppapi_host();
resource_creation_ = host_impl->CreateInProcessResourceCreationAPI(this);
......
}
这个函数定义在文件external/chromium_org/content/renderer/pepper/pepper_plugin_instance_impl.cc中。
PepperPluginInstanceImpl类的构造函数首先将参数module和instance_interface指向的PluginModule对象和PPP_Instance_Combined对象分别保存在成员变量module_和instanceinterface,接下来又会通过参数module指向的PluginModule对象获得一个RendererPpapiHostImpl对象。这个RendererPpapiHostImpl对象是用来在Render进程中描述一个Plugin进程的,它是在Plugin Module的加载过程中创建的,具体可以参考前面Chromium的Plugin进程启动过程分析一文。有了这个RendererPpapiHostImpl对象之后,PepperPluginInstanceImpl类的构造函数就调用它的成员函数CreateInProcessResourceCreationAPI创建一个资源创建接口,如下所示:
scoped_ptr<ppapi::thunk::ResourceCreationAPI>
RendererPpapiHostImpl::CreateInProcessResourceCreationAPI(
PepperPluginInstanceImpl* instance) {
return scoped_ptr<ppapi::thunk::ResourceCreationAPI>(
new PepperInProcessResourceCreation(this, instance));
}
这个函数定义在文件external/chromium_org/content/renderer/pepper/renderer_ppapi_host_impl.cc中。
从这里可以看到,这个资源创建接口是通过一个PepperInProcessResourceCreation对象描述的。这个PepperInProcessResourceCreation对象返回给PepperPluginInstanceImpl类的构造函数之后,将会保存在后者的成员变量resource_creation_中。在接下来一篇文章分析Plugin的3D渲染过程时,我们就会看到这个接口的使用过程。
这一步执行完成后,回到PepperWebPluginImpl类的成员函数initialize中,这时候它就创建了一个Plugin Instance Proxy,也就是一个PepperPluginInstanceImpl对象。接下来PepperWebPluginImpl类的成员函数initialize又会调用上述PepperPluginInstanceImpl对象的成员函数Initialize对其进行初始化。在初始化的过程中,就会请求目标Plugin进程创建一个Plugin Instance,如下所示:
bool PepperPluginInstanceImpl::Initialize(
const std::vector<std::string>& arg_names,
const std::vector<std::string>& arg_values,
bool full_frame) {
......
argn_ = arg_names;
argv_ = arg_values;
scoped_ptr<const char * []> argn_array(StringVectorToArgArray(argn_));
scoped_ptr<const char * []> argv_array(StringVectorToArgArray(argv_));
bool success = PP_ToBool(instance_interface_->DidCreate(
pp_instance(), argn_.size(), argn_array.get(), argv_array.get()));
......
return success;
}
这个函数定义在文件external/chromium_org/content/renderer/pepper/pepper_plugin_instance_impl.cc中。
从前面的分析可以知道,PepperPluginInstanceImpl类的成员变量instance_interface_指向的是一个PPP_Instance_Combined对象。PepperPluginInstanceImpl类的成员函数Initialize主要是调用这个PPP_Instance_Combined对象的成员函数DidCreate请求在目标Plugin进程中创建一个Plugin Instance,如下所示:
PP_Bool PPP_Instance_Combined::DidCreate(PP_Instance instance,
uint32_t argc,
const char* argn[],
const char* argv[]) {
return CallWhileUnlocked(instance_1_1_.DidCreate, instance, argc, argn, argv);
}
这个函数定义在文件external/chromium_org/ppapi/shared_impl/ppp_instance_combined.cc中。
从前面的分析可以知道,PPP_Instance_Combined类的成员变量instance_1_1_指向的是一个PPP_Instance_1_1对象。这个PPP_Instance_1_1对象描述的是一个PPP_INSTANCE_INTERFACE_1_1接口。PPP_Instance_Combined类的成员函数DidCreate通过一个帮助函数CallWhilleUnlocked调用这个PPP_INSTANCE_INTERFACE_1_1接口提供的函数DidCreate,以便请求目标Plugin进程创建一个Plugin Instance。
从前面的分析还可以知道,上述PPP_INSTANCE_INTERFACE_1_1接口提供的函数DidCreate实现在ppp_instance_proxy.cc文件中,如下所示:
PP_Bool DidCreate(PP_Instance instance,
uint32_t argc,
const char* argn[],
const char* argv[]) {
std::vector<std::string> argn_vect;
std::vector<std::string> argv_vect;
for (uint32_t i = 0; i < argc; i++) {
argn_vect.push_back(std::string(argn[i]));
argv_vect.push_back(std::string(argv[i]));
}
PP_Bool result = PP_FALSE;
HostDispatcher::GetForInstance(instance)->Send(
new PpapiMsg_PPPInstance_DidCreate(API_ID_PPP_INSTANCE, instance,
argn_vect, argv_vect, &result));
return result;
}
这个函数定义在文件external/chromium_org/ppapi/proxy/ppp_instance_proxy.cc中。
函数DidCreate主要就是向目标Plugin进程发送一个类型为PpapiMsg_PPPInstance_DidCreate的IPC消息。这个IPC消息的Routing ID指定为API_ID_PPP_INSTANCE,表示它发送到目标Plugin进程后,要交给一个ID为API_ID_PPP_INSTANCE的PPAPI接口处理。
目标Plugin进程可以通过参数instance描述的一个PP_Instance对象获得。在Render进程中,每一个Plugin Instance Proxy都关联有一个PP_Instance对象。因此,通过这个PP_Instance对象就可以找到它对应的Plugin Instance Proxy,也就是一个PepperPluginInstanceImpl对象。每一个PepperPluginInstanceImpl对象又对应有一个Plugin Moudle。该Plugin Module所加载在的Plugin进程即为目标进程。知道了目标Plugin进程之后,就可以通过之前它在启动时与Render进程建立的Plugin通道向它发送IPC消息了。
从前面Chromium的Plugin进程启动过程分析一文可以知道,每一个Plugin进程都存在一个PluginDispatcher对象。Plugin进程将会通过这个PluginDispatcher对象的成员函数OnMessageReceived接收Render进程发送过来的IPC消息。这意味着前面从Render进程发送过来的类型为PpapiMsg_PPPInstance_DidCreate的IPC消息是通过PluginDispatcher类的成员函数OnMessageReceived接收的。
PluginDispatcher类的成员函数OnMessageReceived是从父类Dispatcher继承下来的,它的实现如下所示:
bool Dispatcher::OnMessageReceived(const IPC::Message& msg) {
......
InterfaceProxy* proxy = GetInterfaceProxy(
static_cast<ApiID>(msg.routing_id()));
......
return proxy->OnMessageReceived(msg);
}
这个函数定义在文件external/chromium_org/ppapi/proxy/dispatcher.cc中。
从前面的分析可以知道,此时参数msg指向的Message对象描述的是一个类型为PpapiMsg_PPPInstance_DidCreate的IPC消息,该消息的Routing ID为API_ID_PPP_INSTANCE。这个Routing ID描述的实际上是一个接口ID,Dispatcher类的成员函数OnMessageReceived通过调用另外一个成员函数GetInterfaceProxy可以获得该接口,如下所示:
InterfaceProxy* Dispatcher::GetInterfaceProxy(ApiID id) {
InterfaceProxy* proxy = proxies_[id].get();
if (!proxy) {
// Handle the first time for a given API by creating the proxy for it.
InterfaceProxy::Factory factory =
InterfaceList::GetInstance()->GetFactoryForID(id);
......
proxy = factory(this);
DCHECK(proxy);
proxies_[id].reset(proxy);
}
return proxy;
}
这个函数定义在文件external/chromium_org/ppapi/proxy/dispatcher.cc中。
Dispatcher类的成员函数GetInterfaceProxy首先在成员变量proxies_描述的数组中检查是否存在一个与ID为参数id的接口。如果存在,那么就直接将它返回给调用者。如果不存在,那么就会通过调用Plugin进程中的一个InterfaceList单例对象的成员函数GetFactoryForID检查它内部是否存在该接口。如果存在,那么就会获得一个类型为InterfaceProxy::Factory的函数。调用该函数将会获得一个InterfaceProxy对象。这个InterfaceProxy对象描述的就是一个ID为参数id的接口。
Dispatcher类的成员函数GetInterfaceProxy在将获得的PPAPI接口返回给调用者之前,还会将其缓存在成员变量proxies_描述的数组中,以便以后可以直接获得,而不用通过Plugin进程中的InterfaceList单例对象获得。
接下来,我们继续分析InterfaceList类的成员函数GetFactoryForID的实现,以便了解它返回的InterfaceProxy::Factory函数的实现,如下所示:
InterfaceProxy::Factory InterfaceList::GetFactoryForID(ApiID id) const {
int index = static_cast<int>(id);
......
return id_to_factory_[index];
}
这个函数定义在文件external/chromium_org/ppapi/proxy/interface_list.cc中。
从前面的分析可以知道,此时参数id的值等于API_ID_PPP_INSTANCE。从前面Chromium插件(Plugin)模块(Module)加载过程分析一文可以知道,Plugin进程在加载Plugin Module的时候,会在InterfaceList类的成员变量id_to_factory_描述的数组中注册一个ID为API_ID_PPP_INSTANCE的模板函数ProxyFactory
Dispatcher类的成员函数GetInterfaceProxy获得了模板函数ProxyFactory
PPP_Instance_Proxy对象在创建的过程中,将会获得一个PPP_INSTANCE_INTERFACE_1_1接口。这个PPP_INSTANCE_INTERFACE_1_1接口在处理类型为PpapiMsg_PPPInstance_DidCreate的IPC消息的过程中将会使用到。
接下来,我们就继续分析PPP_Instance_Proxy对象的创建过程,也就是它的构造函数的实现,以便了解它获得PPP_INSTANCE_INTERFACE_1_1接口的过程,如下所示:
PPP_Instance_Proxy::PPP_Instance_Proxy(Dispatcher* dispatcher)
: InterfaceProxy(dispatcher) {
if (dispatcher->IsPlugin()) {
......
combined_interface_.reset(PPP_Instance_Combined::Create(
base::Bind(dispatcher->local_get_interface())));
}
}
这个函数定义在文件external/chromium_org/ppapi/proxy/ppp_instance_proxy.cc中。
从前面的调用过程可以知道,参数dispatcher指向的实际上是一个PluginDispatcher对象。调用这个PluginDispatcher对象的成员函数isPlugin得到的返回值为true。这时候PPP_Instance_Proxy类的构造函数会继续调用这个PluginDispatcher对象的成员函数local_get_interface获得一个PPP_GetInterface接口。这个接口是从Plugin Module导出来的。它的获取过程可以参考前面Chromium的Plugin进程启动过程分析一文。
获得了从Plugin Module导出来的PPP_GetInterface接口之后,PPP_Instance_Proxy类的构造函数通过调用PPP_Instance_Combined类的静态成员函数Create将该接口封装在一个PPP_Instance_Combined对象中,如下所示:
PPP_Instance_Combined* PPP_Instance_Combined::Create(
base::Callback<const void*(const char*)> get_interface_func) {
// Try 1.1.
const void* ppp_instance = get_interface_func.Run(PPP_INSTANCE_INTERFACE_1_1);
if (ppp_instance) {
const PPP_Instance_1_1* ppp_instance_1_1 =
static_cast<const PPP_Instance_1_1*>(ppp_instance);
return new PPP_Instance_Combined(*ppp_instance_1_1);
}
// Failing that, try 1.0.
ppp_instance = get_interface_func.Run(PPP_INSTANCE_INTERFACE_1_0);
if (ppp_instance) {
const PPP_Instance_1_0* ppp_instance_1_0 =
static_cast<const PPP_Instance_1_0*>(ppp_instance);
return new PPP_Instance_Combined(*ppp_instance_1_0);
}
// No supported PPP_Instance version found.
return NULL;
}
这个函数定义在文件external/chromium_org/ppapi/shared_impl/ppp_instance_combined.cc中。
前面我们已经分析过PPP_Instance_Combined类的静态成员函数Create的实现了。不过,这时候参数get_interface_func描述的是一个从Plugin Module导出来的PPP_GetInterface接口。从前面的分析可以知道,PPP_Instance_Combined类的静态成员函数Create最终会通过这个PPP_GetInterface接口获得另外一个类型为PPP_INSTANCE_INTERFACE_1_1的接口。
Plugin Module导出来的PPP_GetInterface接口的实现如下所示:
PP_EXPORT const void* PPP_GetInterface(const char* interface_name) {
if (g_module_singleton)
return g_module_singleton->GetPluginInterface(interface_name);
......
return NULL;
}
这个函数定义在文件external/chromium_org/ppapi/cpp/ppp_entrypoints.cc中。
从前面Chromium插件(Plugin)模块(Module)加载过程分析一文可以知道,g_module_singleton是一个全局变量,它指向的是一个pp::Module对象。这个pp::Module对象描述的就是在当前Plugin进程中加载的Plugin Module。对于我们在前面Chromium插件(Plugin)机制简要介绍和学习计划一文中提到的GLES2 Example来说,这个pp::Module对象的实际类型为GLES2DemoModule。
PPP_GetInterface接口主要就是调用上述pp::Module对象的成员函数GetPluginInterface获得与参数interface_name对应的接口。从前面的分析可以知道,此时参数interface_name的值等于PPP_INSTANCE_INTERFACE_1_1,也就是此时PPP_GetInterface接口是通过调用pp::Module类的成员函数GetPluginInterface获得PPP_INSTANCE_INTERFACE_1_1接口的,如下所示:
const void* Module::GetPluginInterface(const char* interface_name) {
......
if (strcmp(interface_name, PPP_INSTANCE_INTERFACE) == 0)
return &instance_interface;
......
return NULL;
}
这个函数定义在文件external/chromium_org/ppapi/cpp/module.cc中。
PPP_INSTANCE_INTERFACE是一个宏,它的定义为:
#define PPP_INSTANCE_INTERFACE PPP_INSTANCE_INTERFACE_1_1
这个宏定义在文件external/chromium_org/ppap/c/ppp_instance.h
回到pp::Module类的成员函数GetPluginInterface中,由于此时参数interface_name的值等于PPP_INSTANCE_INTERFACE_1_1,因此这时候pp::Module类的成员函数GetPluginInterface会将全局变量instance_interface描述的一个PPP_Instance对象给调用者。这个PPP_Instance对象的定义如下所示:
static PPP_Instance instance_interface = {
&Instance_DidCreate,
&Instance_DidDestroy,
&Instance_DidChangeView,
&Instance_DidChangeFocus,
&Instance_HandleDocumentLoad
};
这个PPP_Instance对象定义在文件external/chromium_org/ppapi/cpp/module.cc中。
从这里就可以看到,Plugin进程中的PPP_INSTANCE_INTERFACE_1_1接口的实现。例如,它的成员变量DidCreate是一个函数指针,指向的函数为Instance_DidCreate,它是用来在Plugin进程中创建一个Plugin Instance的。
这一步执行完成后,回到PPP_Instance_Combined类的静态成员函数Create中,这时候它就会将上述PPP_Instance对象封装在一个PPP_Instance_Combined对象中,并且将该PPP_Instance_Combined对象返回给调用者,即PPP_Instance_Proxy类的构造函数的实现,后者再将该PPP_Instance_Combined对象保存在成员变量combined_interface_中。
这样,我们就分析完成了Dispatcher类的成员函数GetInterfaceProxy通过模板函数ProxyFactory
bool PPP_Instance_Proxy::OnMessageReceived(const IPC::Message& msg) {
......
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(PPP_Instance_Proxy, msg)
IPC_MESSAGE_HANDLER(PpapiMsg_PPPInstance_DidCreate,
OnPluginMsgDidCreate)
......
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
这个函数定义在文件external/chromium_org/ppapi/proxy/ppp_instance_proxy.cc中。
PPP_Instance_Proxy类的成员函数OnMessageReceived将类型为PpapiMsg_PPPInstance_DidCreate的IPC消息分发给成员函数OnPluginMsgDidCreate处理,如下所示:
void PPP_Instance_Proxy::OnPluginMsgDidCreate(
PP_Instance instance,
const std::vector<std::string>& argn,
const std::vector<std::string>& argv,
PP_Bool* result) {
*result = PP_FALSE;
......
// Make sure the arrays always have at least one element so we can take the
// address below.
std::vector<const char*> argn_array(
std::max(static_cast<size_t>(1), argn.size()));
std::vector<const char*> argv_array(
std::max(static_cast<size_t>(1), argn.size()));
for (size_t i = 0; i < argn.size(); i++) {
argn_array[i] = argn[i].c_str();
argv_array[i] = argv[i].c_str();
}
DCHECK(combined_interface_.get());
*result = combined_interface_->DidCreate(instance,
static_cast<uint32_t>(argn.size()),
&argn_array[0], &argv_array[0]);
}
这个函数定义在文件external/chromium_org/ppapi/proxy/ppp_instance_proxy.cc中。
从前面的分析可以知道,PPP_Instance_Proxy类的成员变量combined_interface_指向的一个PPP_Instance_Combined对象。PPP_Instance_Proxy类的成员函数OnPluginMsgDidCreate主要是调用这个PPP_Instance_Combined对象的成员函数DidCreate在当前进程(Plugin进程)中创建一个Plugin Instance,如下所示:
PP_Bool PPP_Instance_Combined::DidCreate(PP_Instance instance,
uint32_t argc,
const char* argn[],
const char* argv[]) {
return CallWhileUnlocked(instance_1_1_.DidCreate, instance, argc, argn, argv);
}
这个函数定义在文件external/chromium_org/ppapi/shared_impl/ppp_instance_combined.cc中。
从前面的分析可以知道,PPP_Instance_Combined类的成员变量instance_1_1_指向的是一个PPP_Instance对象。这个PPP_Instance对象的成员变量DidCreate是一个函数指针,它指向的函数为Instance_DidCreate。PPP_Instance_Combined类的成员函数DidCreate主要是调用这个函数来创建一个Plugin Instance。因此,接下来我们继续分析函数Instance_DidCreate的实现,如下所示:
PP_Bool Instance_DidCreate(PP_Instance pp_instance,
uint32_t argc,
const char* argn[],
const char* argv[]) {
Module* module_singleton = Module::Get();
if (!module_singleton)
return PP_FALSE;
Instance* instance = module_singleton->CreateInstance(pp_instance);
if (!instance)
return PP_FALSE;
module_singleton->current_instances_[pp_instance] = instance;
return PP_FromBool(instance->Init(argc, argn, argv));
}
这个函数定义在文件external/chromium_org/ppapi/cpp/module.cc中。
函数Instance_DidCreate首先调用Module类的静态成员函数Get获得当前Plugin进程中的一个pp::Module单例对象。从前面Chromium插件(Plugin)模块(Module)加载过程分析一文可以知道,这个pp::Module单例对象描述的就是在当前Plugin进程中加载的Plugin Module。有了这个pp::Module对象之后,就可以调用它的成员函数CreateInstance创建一个Plugin Instance,即一个pp::Instance对象。
参数pp_instance的类型为PP_Instance。PP_Instance的实际类型为int32_t,也就是它描述的是一个有符号整数。这个有符号整数指定为当前创建的Plugin Instance的ID。与此同时,创建出来的Plugin Instance将会以它的ID为键值,保存在一个std::map中。这个std::map由上述获得的pp::Module单例对象的成员变量current_instances_描述。因此,通过这个std::map,我们就可以知道一个Plugin Module创建了多少个Plugin Instance。
我们在开发一个Plugin的时候,会自定义一个pp::Module类。例如,在前面Chromium插件(Plugin)机制简要介绍和学习计划一文提到的GLES2 Example,它自定义的pp::Module类为GLES2DemoModule。自定义的 GLES2DemoModule类是从pp::Module类继承下来的,并且会重写成员函数CreateInstance。这意味着前面所创建的Plugin Instance的实际类型由自定义的pp::Module类决定的。
例如,GLES2DemoModule类的成员函数CreateInstance创建的Plugin Instance的实际类型为GLES2DemoInstance,如下所示:
class GLES2DemoModule : public pp::Module {
public:
......
virtual pp::Instance* CreateInstance(PP_Instance instance) {
return new GLES2DemoInstance(instance, this);
}
};
这个函数定义在文件external/chromium_org/ppapi/examples/gles2/gles2.cc中。
至此,Chromium的Render进程就请求目标Plugin进程创建了一个Plugin Instance。这个Plugin Instance的实际类型由开发者定义,只要它是从pp::Instance类继承下来即可。同时,在Render进程当中,存在一个对应的Plugin Instance Proxy。这个Plugin Instance Proxy是通过PepperPluginInstanceImpl类描述的。以后在Render进程中加载的网页需要使用Plugin Instance的功能时,就可以通过Plugin Instance Proxy实现,而在Plugin进程中创建的Plugin Instance需要使用Chromium提供的功能时,可以通过Chromium提供的接口(Browser Interface)实现。
在接下来一篇文章中,我们将以GLES2 Example为例,分析Plugin Instance通过Chromium提供的3D接口渲染自己的UI的过程。通过这个过程,我们就可以看到Plugin Instance与网页的交互过程,从而更好的理解Chromium的Plugin机制。敬请关注!更多的信息也可以关注老罗的新浪微薄:http://weibo.com/shengyangluo。
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8