| // 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_ |