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