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