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