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 #ifndef PPAPI_PROXY_ENTER_PROXY_H_
      6 #define PPAPI_PROXY_ENTER_PROXY_H_
      7 
      8 #include "base/logging.h"
      9 #include "ppapi/cpp/completion_callback.h"
     10 #include "ppapi/proxy/host_dispatcher.h"
     11 #include "ppapi/proxy/plugin_dispatcher.h"
     12 #include "ppapi/proxy/plugin_globals.h"
     13 #include "ppapi/proxy/plugin_resource_tracker.h"
     14 #include "ppapi/thunk/enter.h"
     15 
     16 namespace ppapi {
     17 
     18 namespace proxy {
     19 
     20 // Wrapper around EnterResourceNoLock that takes a host resource. This is used
     21 // when handling messages in the plugin from the host and we need to convert to
     22 // an object in the plugin side corresponding to that.
     23 //
     24 // This never locks since we assume the host Resource is coming from IPC, and
     25 // never logs errors since we assume the host is doing reasonable things.
     26 template<typename ResourceT>
     27 class EnterPluginFromHostResource
     28     : public thunk::EnterResourceNoLock<ResourceT> {
     29  public:
     30   explicit EnterPluginFromHostResource(const HostResource& host_resource)
     31       : thunk::EnterResourceNoLock<ResourceT>(
     32             PluginGlobals::Get()->plugin_resource_tracker()->
     33                 PluginResourceForHostResource(host_resource),
     34             false) {
     35     // Validate that we're in the plugin rather than the host. Otherwise this
     36     // object will do the wrong thing. In the plugin, the instance should have
     37     // a corresponding plugin dispatcher (assuming the resource is valid).
     38     DCHECK(this->failed() ||
     39            PluginDispatcher::GetForInstance(host_resource.instance()));
     40   }
     41 };
     42 
     43 template<typename ResourceT>
     44 class EnterHostFromHostResource
     45     : public thunk::EnterResourceNoLock<ResourceT> {
     46  public:
     47   explicit EnterHostFromHostResource(const HostResource& host_resource)
     48       : thunk::EnterResourceNoLock<ResourceT>(host_resource.host_resource(),
     49                                               false) {
     50     // Validate that we're in the host rather than the plugin. Otherwise this
     51     // object will do the wrong thing. In the host, the instance should have
     52     // a corresponding host disptacher (assuming the resource is valid).
     53     DCHECK(this->failed() ||
     54            HostDispatcher::GetForInstance(host_resource.instance()));
     55   }
     56 
     57   EnterHostFromHostResource(const HostResource& host_resource,
     58                             const pp::CompletionCallback& callback)
     59       : thunk::EnterResourceNoLock<ResourceT>(host_resource.host_resource(),
     60                                               callback.pp_completion_callback(),
     61                                               false) {
     62     // Validate that we're in the host rather than the plugin. Otherwise this
     63     // object will do the wrong thing. In the host, the instance should have
     64     // a corresponding host disptacher (assuming the resource is valid).
     65     DCHECK(this->failed() ||
     66            HostDispatcher::GetForInstance(host_resource.instance()));
     67   }
     68 };
     69 
     70 // Enters a resource and forces a completion callback to be issued.
     71 //
     72 // This is used when implementing the host (renderer) side of a resource
     73 // function that issues a completion callback. In all cases, we need to issue
     74 // the callback to avoid hanging the plugin.
     75 //
     76 // This class automatically constructs a callback with the given factory
     77 // calling the given method. The method will generally be the one that sends
     78 // the message to trigger the completion callback in the plugin process.
     79 //
     80 // It will automatically issue the callback with PP_ERROR_NOINTERFACE if the
     81 // host resource is invalid (i.e. failed() is set). In all other cases you
     82 // should call SetResult(), which will issue the callback immediately if the
     83 // result value isn't PP_OK_COMPLETIONPENDING. In the "completion pending"
     84 // case, it's assumed the function the proxy is calling will take responsibility
     85 // of executing the callback (returned by callback()).
     86 //
     87 // Example:
     88 //   EnterHostFromHostResourceForceCallback<PPB_Foo_API> enter(
     89 //       resource, callback_factory_, &MyClass::SendResult, resource);
     90 //   if (enter.failed())
     91 //     return;  // SendResult automatically called with PP_ERROR_BADRESOURCE.
     92 //   enter.SetResult(enter.object()->DoFoo(enter.callback()));
     93 //
     94 // Where DoFoo's signature looks like this:
     95 //   int32_t DoFoo(PP_CompletionCallback callback);
     96 // And SendResult's implementation looks like this:
     97 //   void MyClass::SendResult(int32_t result, const HostResource& res) {
     98 //     Send(new FooMsg_FooComplete(..., result, res));
     99 //   }
    100 template<typename ResourceT>
    101 class EnterHostFromHostResourceForceCallback
    102     : public EnterHostFromHostResource<ResourceT> {
    103  public:
    104   EnterHostFromHostResourceForceCallback(
    105       const HostResource& host_resource,
    106       const pp::CompletionCallback& callback)
    107       : EnterHostFromHostResource<ResourceT>(host_resource, callback),
    108         needs_running_(true) {
    109   }
    110 
    111   // For callbacks that take no parameters except the "int32_t result". Most
    112   // implementations will use the 1-extra-argument constructor below.
    113   template<class CallbackFactory, typename Method>
    114   EnterHostFromHostResourceForceCallback(
    115       const HostResource& host_resource,
    116       CallbackFactory& factory,
    117       Method method)
    118       : EnterHostFromHostResource<ResourceT>(host_resource,
    119             factory.NewOptionalCallback(method)),
    120         needs_running_(true) {
    121     if (this->failed())
    122       RunCallback(PP_ERROR_BADRESOURCE);
    123   }
    124 
    125   // For callbacks that take an extra parameter as a closure.
    126   template<class CallbackFactory, typename Method, typename A>
    127   EnterHostFromHostResourceForceCallback(
    128       const HostResource& host_resource,
    129       CallbackFactory& factory,
    130       Method method,
    131       const A& a)
    132       : EnterHostFromHostResource<ResourceT>(host_resource,
    133             factory.NewOptionalCallback(method, a)),
    134         needs_running_(true) {
    135     if (this->failed())
    136       RunCallback(PP_ERROR_BADRESOURCE);
    137   }
    138 
    139   // For callbacks that take two extra parameters as a closure.
    140   template<class CallbackFactory, typename Method, typename A, typename B>
    141   EnterHostFromHostResourceForceCallback(
    142       const HostResource& host_resource,
    143       CallbackFactory& factory,
    144       Method method,
    145       const A& a,
    146       const B& b)
    147       : EnterHostFromHostResource<ResourceT>(host_resource,
    148             factory.NewOptionalCallback(method, a, b)),
    149         needs_running_(true) {
    150     if (this->failed())
    151       RunCallback(PP_ERROR_BADRESOURCE);
    152   }
    153 
    154   // For callbacks that take three extra parameters as a closure.
    155   template<class CallbackFactory, typename Method, typename A, typename B,
    156            typename C>
    157   EnterHostFromHostResourceForceCallback(
    158       const HostResource& host_resource,
    159       CallbackFactory& factory,
    160       Method method,
    161       const A& a,
    162       const B& b,
    163       const C& c)
    164       : EnterHostFromHostResource<ResourceT>(host_resource,
    165             factory.NewOptionalCallback(method, a, b, c)),
    166         needs_running_(true) {
    167     if (this->failed())
    168       RunCallback(PP_ERROR_BADRESOURCE);
    169   }
    170 
    171   ~EnterHostFromHostResourceForceCallback() {
    172     if (needs_running_) {
    173       NOTREACHED() << "Should always call SetResult except in the "
    174                       "initialization failed case.";
    175       RunCallback(PP_ERROR_FAILED);
    176     }
    177   }
    178 
    179   void SetResult(int32_t result) {
    180     DCHECK(needs_running_) << "Don't call SetResult when there already is one.";
    181     if (result != PP_OK_COMPLETIONPENDING)
    182       RunCallback(result);
    183     needs_running_ = false;
    184     // Either we already ran the callback, or it will be run asynchronously. We
    185     // clear the callback so it isn't accidentally run again (and because
    186     // EnterBase checks that the callback has been cleared).
    187     this->ClearCallback();
    188   }
    189 
    190  private:
    191   void RunCallback(int32_t result) {
    192     DCHECK(needs_running_);
    193     needs_running_ = false;
    194     this->callback()->Run(result);
    195     this->ClearCallback();
    196   }
    197 
    198   bool needs_running_;
    199 };
    200 
    201 }  // namespace proxy
    202 }  // namespace ppapi
    203 
    204 #endif  // PPAPI_PROXY_ENTER_PROXY_H_
    205