Chromium插件(Plugin)实例(Instance)创建过程分析

Android社区 收藏文章

Chromium在解析网页时,每遇到一个标签,就会创建一个Plugin Instance。一般来说,Plugin Instance是在Plugin进程中创建和运行的。一个Plugin Module对应一个Plugin进程,同时可以创建多个不同的Plugin Instance。前面我们已经分析Plugin Module的加载过程了,本文继续分析Plugin Instance的创建过程。

Plugin Instance在运行的过程中,需要调用Chromium提供的API。这些API是实现在Render进程中的。与此同时,Render进程也需要主动与Plugin Instance通信,例如当网页的标签发生变化时,需要向它对应的Plugin Instance发出通知。为了方便Plugin Instance与Render进程相互通信,每一个Plugin Instance在Render进程中都对应有一个Proxy。这些Proxy通过PepperPluginInstanceImpl类描述。

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对象之后,就会调用这个PepperWebPluginImpl对象的成员函数initialize执行初始化工作。在初始化的过程中,就会创建Plugin Instance。因此,接下来我们就从PepperWebPluginImpl类的成员函数initialize开始,分析在Plugin Instance的创建过程.

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。以后调用这个接口就相当于调用模板函数ProxyFactory,并且会在调用后得到一个PPB_Instance_Proxy对象。

接口API_ID_PPB_GRAPHICS_3D由语句PROXIED_API(PPB_Graphics3D)定义。它的定义过程与接口API_ID_PPB_INSTANCE是一样的,因此,我们容易知道,实现这个接口的是另外一个模板函数ProxyFactory。以后调用这个接口就相当于调用模板函数ProxyFactory,并且会在调用后得到一个PPB_Graphics3D_Proxy对象。

回到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。因此,这时候InterfaceList类的成员函数GetFactoryForID将会返回模板函数ProxyFactory给调用者,也就是Dispatcher类的成员函数GetInterfaceProxy。

Dispatcher类的成员函数GetInterfaceProxy获得了模板函数ProxyFactory之后,就会调用它创建一个PPP_Instance_Proxy对象。这个PPP_Instance_Proxy对象以后就会负责处理Routing ID为API_ID_PPP_INSTANCE的IPC消息。

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创建PPP_Instance_Proxy对象的过程。这个PPP_Instance_Proxy对象会返回给Dispatcher类的另外一个成员函数OnMessageReceived,后者会将前面从Render进程发送过来的类型为PpapiMsg_PPPInstance_DidCreate的IPC消息分发给该PPP_Instance_Proxy对象的成员函数OnMessageReceived处理,如下所示:

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

相关标签

扫一扫

在手机上阅读