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