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/thunk/enter.h" 6 7 #include "base/bind.h" 8 #include "base/logging.h" 9 #include "base/message_loop/message_loop.h" 10 #include "base/strings/stringprintf.h" 11 #include "base/synchronization/lock.h" 12 #include "ppapi/c/pp_errors.h" 13 #include "ppapi/shared_impl/ppapi_globals.h" 14 #include "ppapi/shared_impl/tracked_callback.h" 15 #include "ppapi/thunk/ppb_instance_api.h" 16 #include "ppapi/thunk/resource_creation_api.h" 17 18 namespace ppapi { 19 namespace { 20 21 bool IsMainThread() { 22 return 23 PpapiGlobals::Get()->GetMainThreadMessageLoop()->BelongsToCurrentThread(); 24 } 25 26 } // namespace 27 28 namespace thunk { 29 30 namespace subtle { 31 32 void AssertLockHeld() { 33 base::Lock* proxy_lock = PpapiGlobals::Get()->GetProxyLock(); 34 // The lock is only valid in the plugin side of the proxy, so it only makes 35 // sense to assert there. Otherwise, silently succeed. 36 if (proxy_lock) 37 proxy_lock->AssertAcquired(); 38 } 39 40 EnterBase::EnterBase() 41 : resource_(NULL), 42 retval_(PP_OK) { 43 } 44 45 EnterBase::EnterBase(PP_Resource resource) 46 : resource_(GetResource(resource)), 47 retval_(PP_OK) { 48 } 49 50 EnterBase::EnterBase(PP_Instance instance, SingletonResourceID resource_id) 51 : resource_(GetSingletonResource(instance, resource_id)), 52 retval_(PP_OK) { 53 } 54 55 EnterBase::EnterBase(PP_Resource resource, 56 const PP_CompletionCallback& callback) 57 : resource_(GetResource(resource)), 58 retval_(PP_OK) { 59 callback_ = new TrackedCallback(resource_, callback); 60 } 61 62 EnterBase::EnterBase(PP_Instance instance, SingletonResourceID resource_id, 63 const PP_CompletionCallback& callback) 64 : resource_(GetSingletonResource(instance, resource_id)), 65 retval_(PP_OK) { 66 DCHECK(resource_ || !instance); 67 if (!resource_) 68 retval_ = PP_ERROR_BADARGUMENT; 69 callback_ = new TrackedCallback(resource_, callback); 70 } 71 72 EnterBase::~EnterBase() { 73 // callback_ is cleared any time it is run, scheduled to be run, or once we 74 // know it will be completed asynchronously. So by this point it should be 75 // NULL. 76 DCHECK(!callback_.get()) 77 << "|callback_| is not NULL. Did you forget to call " 78 "|EnterBase::SetResult| in the interface's thunk?"; 79 } 80 81 int32_t EnterBase::SetResult(int32_t result) { 82 if (!callback_.get()) { 83 // It doesn't make sense to call SetResult if there is no callback. 84 NOTREACHED(); 85 retval_ = result; 86 return result; 87 } 88 if (result == PP_OK_COMPLETIONPENDING) { 89 retval_ = result; 90 if (callback_->is_blocking()) { 91 DCHECK(!IsMainThread()); // We should have returned an error before this. 92 retval_ = callback_->BlockUntilComplete(); 93 } else { 94 // The callback is not blocking and the operation will complete 95 // asynchronously, so there's nothing to do. 96 retval_ = result; 97 } 98 } else { 99 // The function completed synchronously. 100 if (callback_->is_required()) { 101 // This is a required callback, so we must issue it asynchronously. 102 callback_->PostRun(result); 103 retval_ = PP_OK_COMPLETIONPENDING; 104 } else { 105 // The callback is blocking or optional, so all we need to do is mark 106 // the callback as completed so that it won't be issued later. 107 callback_->MarkAsCompleted(); 108 retval_ = result; 109 } 110 } 111 callback_ = NULL; 112 return retval_; 113 } 114 115 // static 116 Resource* EnterBase::GetResource(PP_Resource resource) { 117 return PpapiGlobals::Get()->GetResourceTracker()->GetResource(resource); 118 } 119 120 // static 121 Resource* EnterBase::GetSingletonResource(PP_Instance instance, 122 SingletonResourceID resource_id) { 123 PPB_Instance_API* ppb_instance = 124 PpapiGlobals::Get()->GetInstanceAPI(instance); 125 if (!ppb_instance) 126 return NULL; 127 128 return ppb_instance->GetSingletonResource(instance, resource_id); 129 } 130 131 void EnterBase::SetStateForCallbackError(bool report_error) { 132 if (PpapiGlobals::Get()->IsHostGlobals()) { 133 // In-process plugins can't make PPAPI calls off the main thread. 134 CHECK(IsMainThread()); 135 } 136 if (callback_.get()) { 137 if (callback_->is_blocking() && IsMainThread()) { 138 // Blocking callbacks are never allowed on the main thread. 139 callback_->MarkAsCompleted(); 140 callback_ = NULL; 141 retval_ = PP_ERROR_BLOCKS_MAIN_THREAD; 142 if (report_error) { 143 std::string message( 144 "Blocking callbacks are not allowed on the main thread."); 145 PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR, 146 std::string(), message); 147 } 148 } else if (!IsMainThread() && 149 callback_->has_null_target_loop() && 150 !callback_->is_blocking()) { 151 // On a non-main thread, there must be a valid target loop for non- 152 // blocking callbacks, or we will have no place to run them. 153 154 // If the callback is required, there's no nice way to tell the plugin. 155 // We can't run their callback asynchronously without a message loop, and 156 // the plugin won't expect any return code other than 157 // PP_OK_COMPLETIONPENDING. So we crash to make the problem more obvious. 158 if (callback_->is_required()) { 159 std::string message("Attempted to use a required callback, but there " 160 "is no attached message loop on which to run the " 161 "callback."); 162 PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR, 163 std::string(), message); 164 LOG(FATAL) << message; 165 } 166 167 callback_->MarkAsCompleted(); 168 callback_ = NULL; 169 retval_ = PP_ERROR_NO_MESSAGE_LOOP; 170 if (report_error) { 171 std::string message( 172 "The calling thread must have a message loop attached."); 173 PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR, 174 std::string(), message); 175 } 176 } 177 } 178 } 179 180 void EnterBase::ClearCallback() { 181 callback_ = NULL; 182 } 183 184 void EnterBase::SetStateForResourceError(PP_Resource pp_resource, 185 Resource* resource_base, 186 void* object, 187 bool report_error) { 188 // Check for callback errors. If we get any, SetStateForCallbackError will 189 // emit a log message. But we also want to check for resource errors. If there 190 // are both kinds of errors, we'll emit two log messages and return 191 // PP_ERROR_BADRESOURCE. 192 SetStateForCallbackError(report_error); 193 194 if (object) 195 return; // Everything worked. 196 197 if (callback_.get() && callback_->is_required()) { 198 callback_->PostRun(static_cast<int32_t>(PP_ERROR_BADRESOURCE)); 199 callback_ = NULL; 200 retval_ = PP_OK_COMPLETIONPENDING; 201 } else { 202 if (callback_.get()) 203 callback_->MarkAsCompleted(); 204 callback_ = NULL; 205 retval_ = PP_ERROR_BADRESOURCE; 206 } 207 208 // We choose to silently ignore the error when the pp_resource is null 209 // because this is a pretty common case and we don't want to have lots 210 // of errors in the log. This should be an obvious case to debug. 211 if (report_error && pp_resource) { 212 std::string message; 213 if (resource_base) { 214 message = base::StringPrintf( 215 "0x%X is not the correct type for this function.", 216 pp_resource); 217 } else { 218 message = base::StringPrintf( 219 "0x%X is not a valid resource ID.", 220 pp_resource); 221 } 222 PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR, 223 std::string(), message); 224 } 225 } 226 227 void EnterBase::SetStateForFunctionError(PP_Instance pp_instance, 228 void* object, 229 bool report_error) { 230 // Check for callback errors. If we get any, SetStateForCallbackError will 231 // emit a log message. But we also want to check for instance errors. If there 232 // are both kinds of errors, we'll emit two log messages and return 233 // PP_ERROR_BADARGUMENT. 234 SetStateForCallbackError(report_error); 235 236 if (object) 237 return; // Everything worked. 238 239 if (callback_.get() && callback_->is_required()) { 240 callback_->PostRun(static_cast<int32_t>(PP_ERROR_BADARGUMENT)); 241 callback_ = NULL; 242 retval_ = PP_OK_COMPLETIONPENDING; 243 } else { 244 if (callback_.get()) 245 callback_->MarkAsCompleted(); 246 callback_ = NULL; 247 retval_ = PP_ERROR_BADARGUMENT; 248 } 249 250 // We choose to silently ignore the error when the pp_instance is null as 251 // for PP_Resources above. 252 if (report_error && pp_instance) { 253 std::string message; 254 message = base::StringPrintf( 255 "0x%X is not a valid instance ID.", 256 pp_instance); 257 PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR, 258 std::string(), message); 259 } 260 } 261 262 } // namespace subtle 263 264 EnterInstance::EnterInstance(PP_Instance instance) 265 : EnterBase(), 266 functions_(PpapiGlobals::Get()->GetInstanceAPI(instance)) { 267 SetStateForFunctionError(instance, functions_, true); 268 } 269 270 EnterInstance::EnterInstance(PP_Instance instance, 271 const PP_CompletionCallback& callback) 272 : EnterBase(0 /* resource */, callback), 273 // TODO(dmichael): This means that the callback_ we get is not associated 274 // even with the instance, but we should handle that for 275 // MouseLock (maybe others?). 276 functions_(PpapiGlobals::Get()->GetInstanceAPI(instance)) { 277 SetStateForFunctionError(instance, functions_, true); 278 } 279 280 EnterInstance::~EnterInstance() { 281 } 282 283 EnterInstanceNoLock::EnterInstanceNoLock(PP_Instance instance) 284 : EnterBase(), 285 functions_(PpapiGlobals::Get()->GetInstanceAPI(instance)) { 286 SetStateForFunctionError(instance, functions_, true); 287 } 288 289 EnterInstanceNoLock::EnterInstanceNoLock( 290 PP_Instance instance, 291 const PP_CompletionCallback& callback) 292 : EnterBase(0 /* resource */, callback), 293 // TODO(dmichael): This means that the callback_ we get is not associated 294 // even with the instance, but we should handle that for 295 // MouseLock (maybe others?). 296 functions_(PpapiGlobals::Get()->GetInstanceAPI(instance)) { 297 SetStateForFunctionError(instance, functions_, true); 298 } 299 300 EnterInstanceNoLock::~EnterInstanceNoLock() { 301 } 302 303 EnterResourceCreation::EnterResourceCreation(PP_Instance instance) 304 : EnterBase(), 305 functions_(PpapiGlobals::Get()->GetResourceCreationAPI(instance)) { 306 SetStateForFunctionError(instance, functions_, true); 307 } 308 309 EnterResourceCreation::~EnterResourceCreation() { 310 } 311 312 EnterResourceCreationNoLock::EnterResourceCreationNoLock(PP_Instance instance) 313 : EnterBase(), 314 functions_(PpapiGlobals::Get()->GetResourceCreationAPI(instance)) { 315 SetStateForFunctionError(instance, functions_, true); 316 } 317 318 EnterResourceCreationNoLock::~EnterResourceCreationNoLock() { 319 } 320 321 } // namespace thunk 322 } // namespace ppapi 323