Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / ppapi / thunk / enter.cc
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   PpapiGlobals::Get()->MarkPluginIsActive();
36 }
37
38 EnterBase::EnterBase(PP_Resource resource)
39     : resource_(GetResource(resource)),
40       retval_(PP_OK) {
41   PpapiGlobals::Get()->MarkPluginIsActive();
42 }
43
44 EnterBase::EnterBase(PP_Instance instance, SingletonResourceID resource_id)
45     : resource_(GetSingletonResource(instance, resource_id)),
46       retval_(PP_OK) {
47   PpapiGlobals::Get()->MarkPluginIsActive();
48 }
49
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();
56 }
57
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();
66 }
67
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?";
75 }
76
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;
83   }
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;
93     }
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;
105     }
106   }
107   callback_ = NULL;
108   return retval_;
109 }
110
111 // static
112 Resource* EnterBase::GetResource(PP_Resource resource) {
113   return PpapiGlobals::Get()->GetResourceTracker()->GetResource(resource);
114 }
115
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;
123
124   return ppb_instance->GetSingletonResource(instance, resource_id);
125 }
126
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());
131   }
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);
143       }
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.
149
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;
161       }
162
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);
171       }
172     }
173   }
174 }
175
176 void EnterBase::ClearCallback() {
177   callback_ = NULL;
178 }
179
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);
189
190   if (object)
191     return;  // Everything worked.
192
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;
202   }
203
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);
217     }
218     PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR,
219                                                 std::string(), message);
220   }
221 }
222
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);
231
232   if (object)
233     return;  // Everything worked.
234
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;
244   }
245
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);
255   }
256 }
257
258 }  // namespace subtle
259
260 EnterInstance::EnterInstance(PP_Instance instance)
261     : EnterBase(),
262       functions_(PpapiGlobals::Get()->GetInstanceAPI(instance)) {
263   SetStateForFunctionError(instance, functions_, true);
264 }
265
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);
274 }
275
276 EnterInstance::~EnterInstance() {
277 }
278
279 EnterInstanceNoLock::EnterInstanceNoLock(PP_Instance instance)
280     : EnterBase(),
281       functions_(PpapiGlobals::Get()->GetInstanceAPI(instance)) {
282   SetStateForFunctionError(instance, functions_, true);
283 }
284
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);
294 }
295
296 EnterInstanceNoLock::~EnterInstanceNoLock() {
297 }
298
299 EnterResourceCreation::EnterResourceCreation(PP_Instance instance)
300     : EnterBase(),
301       functions_(PpapiGlobals::Get()->GetResourceCreationAPI(instance)) {
302   SetStateForFunctionError(instance, functions_, true);
303 }
304
305 EnterResourceCreation::~EnterResourceCreation() {
306 }
307
308 EnterResourceCreationNoLock::EnterResourceCreationNoLock(PP_Instance instance)
309     : EnterBase(),
310       functions_(PpapiGlobals::Get()->GetResourceCreationAPI(instance)) {
311   SetStateForFunctionError(instance, functions_, true);
312 }
313
314 EnterResourceCreationNoLock::~EnterResourceCreationNoLock() {
315 }
316
317 }  // namespace thunk
318 }  // namespace ppapi