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