Chromium插件(Plugin)實例(Instance)創建過程分析

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


所屬標簽

無標簽

25选5玩法中奖