text: // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #ifndef PPAPI_PROXY_ENTER_PROXY_H_
  #define PPAPI_PROXY_ENTER_PROXY_H_
  
  #include <stdint.h>
  
  #include "base/logging.h"
  #include "ppapi/cpp/completion_callback.h"
  #include "ppapi/proxy/host_dispatcher.h"
  #include "ppapi/proxy/plugin_dispatcher.h"
  #include "ppapi/proxy/plugin_globals.h"
  #include "ppapi/proxy/plugin_resource_tracker.h"
  #include "ppapi/thunk/enter.h"
  
  namespace ppapi {
  
  namespace proxy {
  
  // Wrapper around EnterResourceNoLock that takes a host resource. This is used
  // when handling messages in the plugin from the host and we need to convert to
  // an object in the plugin side corresponding to that.
  //
  // This never locks since we assume the host Resource is coming from IPC, and
  // never logs errors since we assume the host is doing reasonable things.
  template<typename ResourceT>
  class EnterPluginFromHostResource
      : public thunk::EnterResourceNoLock<ResourceT> {
   public:
    explicit EnterPluginFromHostResource(const HostResource& host_resource)
        : thunk::EnterResourceNoLock<ResourceT>(
              PluginGlobals::Get()->plugin_resource_tracker()->
                  PluginResourceForHostResource(host_resource),
              false) {
      // Validate that we're in the plugin rather than the host. Otherwise this
      // object will do the wrong thing. In the plugin, the instance should have
      // a corresponding plugin dispatcher (assuming the resource is valid).
      DCHECK(this->failed() ||
             PluginDispatcher::GetForInstance(host_resource.instance()));
    }
  };
  
  template<typename ResourceT>
  class EnterHostFromHostResource
      : public thunk::EnterResourceNoLock<ResourceT> {
   public:
    explicit EnterHostFromHostResource(const HostResource& host_resource)
        : thunk::EnterResourceNoLock<ResourceT>(host_resource.host_resource(),
                                                false) {
      // Validate that we're in the host rather than the plugin. Otherwise this
      // object will do the wrong thing. In the host, the instance should have
      // a corresponding host disptacher (assuming the resource is valid).
      DCHECK(this->failed() ||
             HostDispatcher::GetForInstance(host_resource.instance()));
    }
  
    EnterHostFromHostResource(const HostResource& host_resource,
                              const pp::CompletionCallback& callback)
        : thunk::EnterResourceNoLock<ResourceT>(host_resource.host_resource(),
                                                callback.pp_completion_callback(),
                                                false) {
      // Validate that we're in the host rather than the plugin. Otherwise this
      // object will do the wrong thing. In the host, the instance should have
      // a corresponding host disptacher (assuming the resource is valid).
      DCHECK(this->failed() ||
             HostDispatcher::GetForInstance(host_resource.instance()));
    }
  };
  
  // Enters a resource and forces a completion callback to be issued.
  //
  // This is used when implementing the host (renderer) side of a resource
  // function that issues a completion callback. In all cases, we need to issue
  // the callback to avoid hanging the plugin.
  //
  // This class automatically constructs a callback with the given factory
  // calling the given method. The method will generally be the one that sends
  // the message to trigger the completion callback in the plugin process.
  //
  // It will automatically issue the callback with PP_ERROR_NOINTERFACE if the
  // host resource is invalid (i.e. failed() is set). In all other cases you
  // should call SetResult(), which will issue the callback immediately if the
  // result value isn't PP_OK_COMPLETIONPENDING. In the "completion pending"
  // case, it's assumed the function the proxy is calling will take responsibility
  // of executing the callback (returned by callback()).
  //
  // Example:
  //   EnterHostFromHostResourceForceCallback<PPB_Foo_API> enter(
  //       resource, callback_factory_, &MyClass::SendResult, resource);
  //   if (enter.failed())
  //     return;  // SendResult automatically called with PP_ERROR_BADRESOURCE.
  //   enter.SetResult(enter.object()->DoFoo(enter.callback()));
  //
  // Where DoFoo's signature looks like this:
  //   int32_t DoFoo(PP_CompletionCallback callback);
  // And SendResult's implementation looks like this:
  //   void MyClass::SendResult(int32_t result, const HostResource& res) {
  //     Send(new FooMsg_FooComplete(..., result, res));
  //   }
  template<typename ResourceT>
  class EnterHostFromHostResourceForceCallback
      : public EnterHostFromHostResource<ResourceT> {
   public:
    EnterHostFromHostResourceForceCallback(
        const HostResource& host_resource,
        const pp::CompletionCallback& callback)
        : EnterHostFromHostResource<ResourceT>(host_resource, callback),
          needs_running_(true) {
    }
  
    // For callbacks that take no parameters except the "int32_t result". Most
    // implementations will use the 1-extra-argument constructor below.
    template<class CallbackFactory, typename Method>
    EnterHostFromHostResourceForceCallback(
        const HostResource& host_resource,
        CallbackFactory& factory,
        Method method)
        : EnterHostFromHostResource<ResourceT>(host_resource,
              factory.NewOptionalCallback(method)),
          needs_running_(true) {
      if (this->failed())
        RunCallback(PP_ERROR_BADRESOURCE);
    }
  
    // For callbacks that take an extra parameter as a closure.
    template<class CallbackFactory, typename Method, typename A>
    EnterHostFromHostResourceForceCallback(
        const HostResource& host_resource,
        CallbackFactory& factory,
        Method method,
        const A& a)
        : EnterHostFromHostResource<ResourceT>(host_resource,
              factory.NewOptionalCallback(method, a)),
          needs_running_(true) {
      if (this->failed())
        RunCallback(PP_ERROR_BADRESOURCE);
    }
  
    // For callbacks that take two extra parameters as a closure.
    template<class CallbackFactory, typename Method, typename A, typename B>
    EnterHostFromHostResourceForceCallback(
        const HostResource& host_resource,
        CallbackFactory& factory,
        Method method,
        const A& a,
        const B& b)
        : EnterHostFromHostResource<ResourceT>(host_resource,
              factory.NewOptionalCallback(method, a, b)),
          needs_running_(true) {
      if (this->failed())
        RunCallback(PP_ERROR_BADRESOURCE);
    }
  
    // For callbacks that take three extra parameters as a closure.
    template<class CallbackFactory, typename Method, typename A, typename B,
             typename C>
    EnterHostFromHostResourceForceCallback(
        const HostResource& host_resource,
        CallbackFactory& factory,
        Method method,
        const A& a,
        const B& b,
        const C& c)
        : EnterHostFromHostResource<ResourceT>(host_resource,
              factory.NewOptionalCallback(method, a, b, c)),
          needs_running_(true) {
      if (this->failed())
        RunCallback(PP_ERROR_BADRESOURCE);
    }
  
    ~EnterHostFromHostResourceForceCallback() {
      if (needs_running_) {
        NOTREACHED() << "Should always call SetResult except in the "
                        "initialization failed case.";
        RunCallback(PP_ERROR_FAILED);
      }
    }
  
    void SetResult(int32_t result) {
      DCHECK(needs_running_) << "Don't call SetResult when there already is one.";
      if (result != PP_OK_COMPLETIONPENDING)
        RunCallback(result);
      needs_running_ = false;
      // Either we already ran the callback, or it will be run asynchronously. We
      // clear the callback so it isn't accidentally run again (and because
      // EnterBase checks that the callback has been cleared).
      this->ClearCallback();
    }
  
   private:
    void RunCallback(int32_t result) {
      DCHECK(needs_running_);
      needs_running_ = false;
      this->callback()->Run(result);
      this->ClearCallback();
    }
  
    bool needs_running_;
  };
  
  }  // namespace proxy
  }  // namespace ppapi
  
  #endif  // PPAPI_PROXY_ENTER_PROXY_H_
  