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

发表于 5年以前  | 总阅读数:2881 次

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

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:1年以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:1年以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:1年以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:1年以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:1年以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:1年以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:1年以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:1年以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:1年以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:1年以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:1年以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:1年以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:1年以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:1年以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:1年以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:1年以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:1年以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:1年以前  |  398次阅读  |  详细内容 »
 目录