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: EnterPluginFromHostResource(const HostResource & host_resource)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: EnterHostFromHostResource(const HostResource & host_resource)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 EnterHostFromHostResource(const HostResource & host_resource,const pp::CompletionCallback & callback)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: EnterHostFromHostResourceForceCallback(const HostResource & host_resource,const pp::CompletionCallback & callback)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> EnterHostFromHostResourceForceCallback(const HostResource & host_resource,CallbackFactory & factory,Method 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> EnterHostFromHostResourceForceCallback(const HostResource & host_resource,CallbackFactory & factory,Method method,const A & 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> EnterHostFromHostResourceForceCallback(const HostResource & host_resource,CallbackFactory & factory,Method method,const A & a,const B & 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> EnterHostFromHostResourceForceCallback(const HostResource & host_resource,CallbackFactory & factory,Method method,const A & a,const B & b,const C & 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 ~EnterHostFromHostResourceForceCallback()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 SetResult(int32_t result)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: RunCallback(int32_t result)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