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