Home | History | Annotate | Download | only in proxy
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "ppapi/proxy/ppp_content_decryptor_private_proxy.h"
      6 
      7 #include "base/platform_file.h"
      8 #include "ppapi/c/pp_bool.h"
      9 #include "ppapi/c/ppb_core.h"
     10 #include "ppapi/proxy/content_decryptor_private_serializer.h"
     11 #include "ppapi/proxy/host_dispatcher.h"
     12 #include "ppapi/proxy/plugin_globals.h"
     13 #include "ppapi/proxy/plugin_resource_tracker.h"
     14 #include "ppapi/proxy/ppapi_messages.h"
     15 #include "ppapi/proxy/ppb_buffer_proxy.h"
     16 #include "ppapi/proxy/serialized_var.h"
     17 #include "ppapi/shared_impl/scoped_pp_resource.h"
     18 #include "ppapi/shared_impl/var_tracker.h"
     19 #include "ppapi/thunk/enter.h"
     20 #include "ppapi/thunk/ppb_buffer_api.h"
     21 #include "ppapi/thunk/ppb_instance_api.h"
     22 #include "ppapi/thunk/thunk.h"
     23 
     24 using ppapi::thunk::EnterResourceNoLock;
     25 using ppapi::thunk::PPB_Buffer_API;
     26 using ppapi::thunk::PPB_Instance_API;
     27 
     28 namespace ppapi {
     29 namespace proxy {
     30 
     31 namespace {
     32 
     33 PP_Bool DescribeHostBufferResource(PP_Resource resource, uint32_t* size) {
     34   EnterResourceNoLock<PPB_Buffer_API> enter(resource, true);
     35   if (enter.failed())
     36     return PP_FALSE;
     37   return enter.object()->Describe(size);
     38 }
     39 
     40 // TODO(dmichael): Refactor so this handle sharing code is in one place.
     41 PP_Bool ShareHostBufferResourceToPlugin(
     42     HostDispatcher* dispatcher,
     43     PP_Resource resource,
     44     base::SharedMemoryHandle* shared_mem_handle) {
     45   if (!dispatcher || resource == 0 || !shared_mem_handle)
     46     return PP_FALSE;
     47   EnterResourceNoLock<PPB_Buffer_API> enter(resource, true);
     48   if (enter.failed())
     49     return PP_FALSE;
     50   int handle;
     51   int32_t result = enter.object()->GetSharedMemory(&handle);
     52   if (result != PP_OK)
     53     return PP_FALSE;
     54   base::PlatformFile platform_file =
     55   #if defined(OS_WIN)
     56       reinterpret_cast<HANDLE>(static_cast<intptr_t>(handle));
     57   #elif defined(OS_POSIX)
     58       handle;
     59   #else
     60   #error Not implemented.
     61   #endif
     62 
     63   *shared_mem_handle = dispatcher->ShareHandleWithRemote(platform_file, false);
     64   return PP_TRUE;
     65 }
     66 
     67 // SerializedVarReceiveInput will decrement the reference count, but we want
     68 // to give the recipient a reference. This utility function takes care of that
     69 // work for the message handlers defined below.
     70 PP_Var ExtractReceivedVarAndAddRef(Dispatcher* dispatcher,
     71                                    SerializedVarReceiveInput* serialized_var) {
     72   PP_Var var = serialized_var->Get(dispatcher);
     73   PpapiGlobals::Get()->GetVarTracker()->AddRefVar(var);
     74   return var;
     75 }
     76 
     77 bool InitializePppDecryptorBuffer(PP_Instance instance,
     78                                   HostDispatcher* dispatcher,
     79                                   PP_Resource resource,
     80                                   PPPDecryptor_Buffer* buffer) {
     81   if (!buffer) {
     82     NOTREACHED();
     83     return false;
     84   }
     85 
     86   if (resource == 0) {
     87     buffer->resource = HostResource();
     88     buffer->handle = base::SharedMemoryHandle();
     89     buffer->size = 0;
     90     return true;
     91   }
     92 
     93   HostResource host_resource;
     94   host_resource.SetHostResource(instance, resource);
     95 
     96   uint32_t size = 0;
     97   if (DescribeHostBufferResource(resource, &size) == PP_FALSE)
     98     return false;
     99 
    100   base::SharedMemoryHandle handle;
    101   if (ShareHostBufferResourceToPlugin(dispatcher,
    102                                       resource,
    103                                       &handle) == PP_FALSE)
    104     return false;
    105 
    106   buffer->resource = host_resource;
    107   buffer->handle = handle;
    108   buffer->size = size;
    109   return true;
    110 }
    111 
    112 void Initialize(PP_Instance instance,
    113                 PP_Var key_system) {
    114   HostDispatcher* dispatcher = HostDispatcher::GetForInstance(instance);
    115   if (!dispatcher) {
    116     NOTREACHED();
    117     return;
    118   }
    119 
    120   dispatcher->Send(
    121       new PpapiMsg_PPPContentDecryptor_Initialize(
    122           API_ID_PPP_CONTENT_DECRYPTOR_PRIVATE,
    123           instance,
    124           SerializedVarSendInput(dispatcher, key_system)));
    125 }
    126 
    127 void CreateSession(PP_Instance instance,
    128                    uint32_t session_id,
    129                    PP_Var type,
    130                    PP_Var init_data) {
    131   HostDispatcher* dispatcher = HostDispatcher::GetForInstance(instance);
    132   if (!dispatcher) {
    133     NOTREACHED();
    134     return;
    135   }
    136 
    137   dispatcher->Send(new PpapiMsg_PPPContentDecryptor_CreateSession(
    138       API_ID_PPP_CONTENT_DECRYPTOR_PRIVATE,
    139       instance,
    140       session_id,
    141       SerializedVarSendInput(dispatcher, type),
    142       SerializedVarSendInput(dispatcher, init_data)));
    143 }
    144 
    145 void UpdateSession(PP_Instance instance, uint32_t session_id, PP_Var response) {
    146   HostDispatcher* dispatcher = HostDispatcher::GetForInstance(instance);
    147   if (!dispatcher) {
    148     NOTREACHED();
    149     return;
    150   }
    151 
    152   dispatcher->Send(new PpapiMsg_PPPContentDecryptor_UpdateSession(
    153       API_ID_PPP_CONTENT_DECRYPTOR_PRIVATE,
    154       instance,
    155       session_id,
    156       SerializedVarSendInput(dispatcher, response)));
    157 }
    158 
    159 void ReleaseSession(PP_Instance instance, uint32_t session_id) {
    160   HostDispatcher* dispatcher = HostDispatcher::GetForInstance(instance);
    161   if (!dispatcher) {
    162     NOTREACHED();
    163     return;
    164   }
    165 
    166   dispatcher->Send(new PpapiMsg_PPPContentDecryptor_ReleaseSession(
    167       API_ID_PPP_CONTENT_DECRYPTOR_PRIVATE, instance, session_id));
    168 }
    169 
    170 void Decrypt(PP_Instance instance,
    171              PP_Resource encrypted_block,
    172              const PP_EncryptedBlockInfo* encrypted_block_info) {
    173   HostDispatcher* dispatcher = HostDispatcher::GetForInstance(instance);
    174   if (!dispatcher) {
    175     NOTREACHED();
    176     return;
    177   }
    178 
    179   PPPDecryptor_Buffer buffer;
    180   if (!InitializePppDecryptorBuffer(instance,
    181                                     dispatcher,
    182                                     encrypted_block,
    183                                     &buffer)) {
    184     NOTREACHED();
    185     return;
    186   }
    187 
    188   std::string serialized_block_info;
    189   if (!SerializeBlockInfo(*encrypted_block_info, &serialized_block_info)) {
    190     NOTREACHED();
    191     return;
    192   }
    193 
    194   // PluginResourceTracker in the plugin process assumes that resources that it
    195   // tracks have been addrefed on behalf of the plugin at the renderer side. So
    196   // we explicitly do it for |encryped_block| here.
    197   PpapiGlobals::Get()->GetResourceTracker()->AddRefResource(encrypted_block);
    198 
    199   dispatcher->Send(
    200       new PpapiMsg_PPPContentDecryptor_Decrypt(
    201           API_ID_PPP_CONTENT_DECRYPTOR_PRIVATE,
    202           instance,
    203           buffer,
    204           serialized_block_info));
    205 }
    206 
    207 void InitializeAudioDecoder(
    208     PP_Instance instance,
    209     const PP_AudioDecoderConfig* decoder_config,
    210     PP_Resource extra_data_buffer) {
    211   HostDispatcher* dispatcher = HostDispatcher::GetForInstance(instance);
    212   if (!dispatcher) {
    213     NOTREACHED();
    214     return;
    215   }
    216 
    217   std::string serialized_decoder_config;
    218   if (!SerializeBlockInfo(*decoder_config, &serialized_decoder_config)) {
    219     NOTREACHED();
    220     return;
    221   }
    222 
    223   PPPDecryptor_Buffer buffer;
    224   if (!InitializePppDecryptorBuffer(instance,
    225                                     dispatcher,
    226                                     extra_data_buffer,
    227                                     &buffer)) {
    228     NOTREACHED();
    229     return;
    230   }
    231 
    232   // PluginResourceTracker in the plugin process assumes that resources that it
    233   // tracks have been addrefed on behalf of the plugin at the renderer side. So
    234   // we explicitly do it for |extra_data_buffer| here.
    235   PpapiGlobals::Get()->GetResourceTracker()->AddRefResource(extra_data_buffer);
    236 
    237   dispatcher->Send(
    238       new PpapiMsg_PPPContentDecryptor_InitializeAudioDecoder(
    239           API_ID_PPP_CONTENT_DECRYPTOR_PRIVATE,
    240           instance,
    241           serialized_decoder_config,
    242           buffer));
    243 }
    244 
    245 void InitializeVideoDecoder(
    246     PP_Instance instance,
    247     const PP_VideoDecoderConfig* decoder_config,
    248     PP_Resource extra_data_buffer) {
    249   HostDispatcher* dispatcher = HostDispatcher::GetForInstance(instance);
    250   if (!dispatcher) {
    251     NOTREACHED();
    252     return;
    253   }
    254 
    255   std::string serialized_decoder_config;
    256   if (!SerializeBlockInfo(*decoder_config, &serialized_decoder_config)) {
    257     NOTREACHED();
    258     return;
    259   }
    260 
    261   PPPDecryptor_Buffer buffer;
    262   if (!InitializePppDecryptorBuffer(instance,
    263                                     dispatcher,
    264                                     extra_data_buffer,
    265                                     &buffer)) {
    266     NOTREACHED();
    267     return;
    268   }
    269 
    270   // PluginResourceTracker in the plugin process assumes that resources that it
    271   // tracks have been addrefed on behalf of the plugin at the renderer side. So
    272   // we explicitly do it for |extra_data_buffer| here.
    273   PpapiGlobals::Get()->GetResourceTracker()->AddRefResource(extra_data_buffer);
    274 
    275   dispatcher->Send(
    276       new PpapiMsg_PPPContentDecryptor_InitializeVideoDecoder(
    277           API_ID_PPP_CONTENT_DECRYPTOR_PRIVATE,
    278           instance,
    279           serialized_decoder_config,
    280           buffer));
    281 }
    282 
    283 
    284 void DeinitializeDecoder(PP_Instance instance,
    285                          PP_DecryptorStreamType decoder_type,
    286                          uint32_t request_id) {
    287   HostDispatcher* dispatcher = HostDispatcher::GetForInstance(instance);
    288   if (!dispatcher) {
    289     NOTREACHED();
    290     return;
    291   }
    292 
    293   dispatcher->Send(
    294       new PpapiMsg_PPPContentDecryptor_DeinitializeDecoder(
    295           API_ID_PPP_CONTENT_DECRYPTOR_PRIVATE,
    296           instance,
    297           decoder_type,
    298           request_id));
    299 }
    300 
    301 void ResetDecoder(PP_Instance instance,
    302                   PP_DecryptorStreamType decoder_type,
    303                   uint32_t request_id) {
    304   HostDispatcher* dispatcher = HostDispatcher::GetForInstance(instance);
    305   if (!dispatcher) {
    306     NOTREACHED();
    307     return;
    308   }
    309 
    310   dispatcher->Send(
    311       new PpapiMsg_PPPContentDecryptor_ResetDecoder(
    312           API_ID_PPP_CONTENT_DECRYPTOR_PRIVATE,
    313           instance,
    314           decoder_type,
    315           request_id));
    316 }
    317 
    318 void DecryptAndDecode(PP_Instance instance,
    319                       PP_DecryptorStreamType decoder_type,
    320                       PP_Resource encrypted_buffer,
    321                       const PP_EncryptedBlockInfo* encrypted_block_info) {
    322   HostDispatcher* dispatcher = HostDispatcher::GetForInstance(instance);
    323   if (!dispatcher) {
    324     NOTREACHED();
    325     return;
    326   }
    327 
    328   PPPDecryptor_Buffer buffer;
    329   if (!InitializePppDecryptorBuffer(instance,
    330                                     dispatcher,
    331                                     encrypted_buffer,
    332                                     &buffer)) {
    333     NOTREACHED();
    334     return;
    335   }
    336 
    337   std::string serialized_block_info;
    338   if (!SerializeBlockInfo(*encrypted_block_info, &serialized_block_info)) {
    339     NOTREACHED();
    340     return;
    341   }
    342 
    343   // PluginResourceTracker in the plugin process assumes that resources that it
    344   // tracks have been addrefed on behalf of the plugin at the renderer side. So
    345   // we explicitly do it for |encrypted_buffer| here.
    346   PpapiGlobals::Get()->GetResourceTracker()->AddRefResource(encrypted_buffer);
    347 
    348   dispatcher->Send(
    349       new PpapiMsg_PPPContentDecryptor_DecryptAndDecode(
    350           API_ID_PPP_CONTENT_DECRYPTOR_PRIVATE,
    351           instance,
    352           decoder_type,
    353           buffer,
    354           serialized_block_info));
    355 }
    356 
    357 static const PPP_ContentDecryptor_Private content_decryptor_interface = {
    358   &Initialize,
    359   &CreateSession,
    360   &UpdateSession,
    361   &ReleaseSession,
    362   &Decrypt,
    363   &InitializeAudioDecoder,
    364   &InitializeVideoDecoder,
    365   &DeinitializeDecoder,
    366   &ResetDecoder,
    367   &DecryptAndDecode
    368 };
    369 
    370 }  // namespace
    371 
    372 PPP_ContentDecryptor_Private_Proxy::PPP_ContentDecryptor_Private_Proxy(
    373     Dispatcher* dispatcher)
    374     : InterfaceProxy(dispatcher),
    375       ppp_decryptor_impl_(NULL) {
    376   if (dispatcher->IsPlugin()) {
    377     ppp_decryptor_impl_ = static_cast<const PPP_ContentDecryptor_Private*>(
    378         dispatcher->local_get_interface()(
    379             PPP_CONTENTDECRYPTOR_PRIVATE_INTERFACE));
    380   }
    381 }
    382 
    383 PPP_ContentDecryptor_Private_Proxy::~PPP_ContentDecryptor_Private_Proxy() {
    384 }
    385 
    386 // static
    387 const PPP_ContentDecryptor_Private*
    388     PPP_ContentDecryptor_Private_Proxy::GetProxyInterface() {
    389   return &content_decryptor_interface;
    390 }
    391 
    392 bool PPP_ContentDecryptor_Private_Proxy::OnMessageReceived(
    393     const IPC::Message& msg) {
    394   if (!dispatcher()->IsPlugin())
    395     return false;  // These are only valid from host->plugin.
    396                    // Don't allow the plugin to send these to the host.
    397 
    398   bool handled = true;
    399   IPC_BEGIN_MESSAGE_MAP(PPP_ContentDecryptor_Private_Proxy, msg)
    400     IPC_MESSAGE_HANDLER(PpapiMsg_PPPContentDecryptor_Initialize,
    401                         OnMsgInitialize)
    402     IPC_MESSAGE_HANDLER(PpapiMsg_PPPContentDecryptor_CreateSession,
    403                         OnMsgCreateSession)
    404     IPC_MESSAGE_HANDLER(PpapiMsg_PPPContentDecryptor_UpdateSession,
    405                         OnMsgUpdateSession)
    406     IPC_MESSAGE_HANDLER(PpapiMsg_PPPContentDecryptor_ReleaseSession,
    407                         OnMsgReleaseSession)
    408     IPC_MESSAGE_HANDLER(PpapiMsg_PPPContentDecryptor_Decrypt,
    409                         OnMsgDecrypt)
    410     IPC_MESSAGE_HANDLER(PpapiMsg_PPPContentDecryptor_InitializeAudioDecoder,
    411                         OnMsgInitializeAudioDecoder)
    412     IPC_MESSAGE_HANDLER(PpapiMsg_PPPContentDecryptor_InitializeVideoDecoder,
    413                         OnMsgInitializeVideoDecoder)
    414     IPC_MESSAGE_HANDLER(PpapiMsg_PPPContentDecryptor_DeinitializeDecoder,
    415                         OnMsgDeinitializeDecoder)
    416     IPC_MESSAGE_HANDLER(PpapiMsg_PPPContentDecryptor_ResetDecoder,
    417                         OnMsgResetDecoder)
    418     IPC_MESSAGE_HANDLER(PpapiMsg_PPPContentDecryptor_DecryptAndDecode,
    419                         OnMsgDecryptAndDecode)
    420     IPC_MESSAGE_UNHANDLED(handled = false)
    421   IPC_END_MESSAGE_MAP()
    422   DCHECK(handled);
    423   return handled;
    424 }
    425 
    426 void PPP_ContentDecryptor_Private_Proxy::OnMsgInitialize(
    427     PP_Instance instance,
    428     SerializedVarReceiveInput key_system) {
    429   if (ppp_decryptor_impl_) {
    430     CallWhileUnlocked(
    431         ppp_decryptor_impl_->Initialize,
    432         instance,
    433         ExtractReceivedVarAndAddRef(dispatcher(), &key_system));
    434   }
    435 }
    436 
    437 void PPP_ContentDecryptor_Private_Proxy::OnMsgCreateSession(
    438     PP_Instance instance,
    439     uint32_t session_id,
    440     SerializedVarReceiveInput type,
    441     SerializedVarReceiveInput init_data) {
    442   if (ppp_decryptor_impl_) {
    443     CallWhileUnlocked(ppp_decryptor_impl_->CreateSession,
    444                       instance,
    445                       session_id,
    446                       ExtractReceivedVarAndAddRef(dispatcher(), &type),
    447                       ExtractReceivedVarAndAddRef(dispatcher(), &init_data));
    448   }
    449 }
    450 
    451 void PPP_ContentDecryptor_Private_Proxy::OnMsgUpdateSession(
    452     PP_Instance instance,
    453     uint32_t session_id,
    454     SerializedVarReceiveInput response) {
    455   if (ppp_decryptor_impl_) {
    456     CallWhileUnlocked(ppp_decryptor_impl_->UpdateSession,
    457                       instance,
    458                       session_id,
    459                       ExtractReceivedVarAndAddRef(dispatcher(), &response));
    460   }
    461 }
    462 
    463 void PPP_ContentDecryptor_Private_Proxy::OnMsgReleaseSession(
    464     PP_Instance instance,
    465     uint32_t session_id) {
    466   if (ppp_decryptor_impl_) {
    467     CallWhileUnlocked(ppp_decryptor_impl_->ReleaseSession,
    468                       instance,
    469                       session_id);
    470   }
    471 }
    472 
    473 void PPP_ContentDecryptor_Private_Proxy::OnMsgDecrypt(
    474     PP_Instance instance,
    475     const PPPDecryptor_Buffer& encrypted_buffer,
    476     const std::string& serialized_block_info) {
    477   ScopedPPResource plugin_resource(
    478       ScopedPPResource::PassRef(),
    479       PPB_Buffer_Proxy::AddProxyResource(encrypted_buffer.resource,
    480                                          encrypted_buffer.handle,
    481                                          encrypted_buffer.size));
    482   if (ppp_decryptor_impl_) {
    483     PP_EncryptedBlockInfo block_info;
    484     if (!DeserializeBlockInfo(serialized_block_info, &block_info))
    485       return;
    486     CallWhileUnlocked(ppp_decryptor_impl_->Decrypt,
    487                       instance,
    488                       plugin_resource.get(),
    489                       const_cast<const PP_EncryptedBlockInfo*>(&block_info));
    490   }
    491 }
    492 
    493 void PPP_ContentDecryptor_Private_Proxy::OnMsgInitializeAudioDecoder(
    494     PP_Instance instance,
    495     const std::string& serialized_decoder_config,
    496     const PPPDecryptor_Buffer& extra_data_buffer) {
    497   ScopedPPResource plugin_resource;
    498   if (extra_data_buffer.size > 0) {
    499     plugin_resource = ScopedPPResource(
    500         ScopedPPResource::PassRef(),
    501         PPB_Buffer_Proxy::AddProxyResource(extra_data_buffer.resource,
    502                                            extra_data_buffer.handle,
    503                                            extra_data_buffer.size));
    504   }
    505 
    506   PP_AudioDecoderConfig decoder_config;
    507   if (!DeserializeBlockInfo(serialized_decoder_config, &decoder_config))
    508       return;
    509 
    510   if (ppp_decryptor_impl_) {
    511     CallWhileUnlocked(
    512         ppp_decryptor_impl_->InitializeAudioDecoder,
    513         instance,
    514         const_cast<const PP_AudioDecoderConfig*>(&decoder_config),
    515         plugin_resource.get());
    516   }
    517 }
    518 
    519 void PPP_ContentDecryptor_Private_Proxy::OnMsgInitializeVideoDecoder(
    520     PP_Instance instance,
    521     const std::string& serialized_decoder_config,
    522     const PPPDecryptor_Buffer& extra_data_buffer) {
    523   ScopedPPResource plugin_resource;
    524   if (extra_data_buffer.resource.host_resource() != 0) {
    525     plugin_resource = ScopedPPResource(
    526         ScopedPPResource::PassRef(),
    527         PPB_Buffer_Proxy::AddProxyResource(extra_data_buffer.resource,
    528                                            extra_data_buffer.handle,
    529                                            extra_data_buffer.size));
    530   }
    531 
    532   PP_VideoDecoderConfig decoder_config;
    533   if (!DeserializeBlockInfo(serialized_decoder_config, &decoder_config))
    534       return;
    535 
    536   if (ppp_decryptor_impl_) {
    537     CallWhileUnlocked(
    538         ppp_decryptor_impl_->InitializeVideoDecoder,
    539         instance,
    540         const_cast<const PP_VideoDecoderConfig*>(&decoder_config),
    541         plugin_resource.get());
    542   }
    543 }
    544 
    545 void PPP_ContentDecryptor_Private_Proxy::OnMsgDeinitializeDecoder(
    546     PP_Instance instance,
    547     PP_DecryptorStreamType decoder_type,
    548     uint32_t request_id) {
    549   if (ppp_decryptor_impl_) {
    550     CallWhileUnlocked(
    551         ppp_decryptor_impl_->DeinitializeDecoder,
    552         instance,
    553         decoder_type,
    554         request_id);
    555   }
    556 }
    557 
    558 void PPP_ContentDecryptor_Private_Proxy::OnMsgResetDecoder(
    559     PP_Instance instance,
    560     PP_DecryptorStreamType decoder_type,
    561     uint32_t request_id) {
    562   if (ppp_decryptor_impl_) {
    563     CallWhileUnlocked(
    564         ppp_decryptor_impl_->ResetDecoder,
    565         instance,
    566         decoder_type,
    567         request_id);
    568   }
    569 }
    570 
    571 void PPP_ContentDecryptor_Private_Proxy::OnMsgDecryptAndDecode(
    572     PP_Instance instance,
    573     PP_DecryptorStreamType decoder_type,
    574     const PPPDecryptor_Buffer& encrypted_buffer,
    575     const std::string& serialized_block_info) {
    576   ScopedPPResource plugin_resource;
    577   if (encrypted_buffer.resource.host_resource() != 0) {
    578     plugin_resource = ScopedPPResource(
    579         ScopedPPResource::PassRef(),
    580         PPB_Buffer_Proxy::AddProxyResource(encrypted_buffer.resource,
    581                                            encrypted_buffer.handle,
    582                                            encrypted_buffer.size));
    583   }
    584 
    585   if (ppp_decryptor_impl_) {
    586     PP_EncryptedBlockInfo block_info;
    587     if (!DeserializeBlockInfo(serialized_block_info, &block_info))
    588       return;
    589     CallWhileUnlocked(
    590         ppp_decryptor_impl_->DecryptAndDecode,
    591         instance,
    592         decoder_type,
    593         plugin_resource.get(),
    594         const_cast<const PP_EncryptedBlockInfo*>(&block_info));
    595   }
    596 }
    597 
    598 }  // namespace proxy
    599 }  // namespace ppapi
    600