OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright 2011 (c) The Native Client Authors. All rights reserved. | |
3 * Use of this source code is governed by a BSD-style license that can be | |
4 * found in the LICENSE file. | |
5 */ | |
6 | |
7 | |
8 // Scriptable handle implementation. | |
9 | |
10 #include "native_client/src/trusted/plugin/scriptable_handle.h" | |
11 | |
12 #include <stdio.h> | |
13 #include <string.h> | |
14 | |
15 #include <assert.h> | |
16 #include <set> | |
17 #include <sstream> | |
18 #include <string> | |
19 #include <vector> | |
20 | |
21 | |
22 #include "native_client/src/include/checked_cast.h" | |
23 #include "native_client/src/include/nacl_macros.h" | |
24 #include "native_client/src/include/nacl_string.h" | |
25 #include "native_client/src/include/portability.h" | |
26 #include "native_client/src/shared/platform/nacl_check.h" | |
27 #include "native_client/src/shared/srpc/nacl_srpc.h" | |
28 #include "native_client/src/trusted/plugin/array_ppapi.h" | |
29 #include "native_client/src/trusted/plugin/browser_interface.h" | |
30 #include "native_client/src/trusted/plugin/desc_based_handle.h" | |
31 #include "native_client/src/trusted/plugin/method_map.h" | |
32 #include "native_client/src/trusted/plugin/plugin.h" | |
33 #include "native_client/src/trusted/plugin/utility.h" | |
34 #include "native_client/src/trusted/plugin/var_utils.h" | |
35 | |
36 | |
37 namespace plugin { | |
38 | |
39 namespace { | |
40 | |
41 // For security we keep track of the set of scriptable handles that were | |
42 // created. | |
43 | |
44 std::set<const plugin::ScriptableHandle*>* g_ValidHandles = 0; | |
45 | |
46 void RememberValidHandle(const ScriptableHandle* handle) { | |
47 // Initialize the set. | |
48 // BUG: this is not thread safe. We may leak sets, or worse, may not | |
49 // correctly insert valid handles into the set. | |
50 // TODO(sehr): use pthread_once or similar initialization. | |
51 if (NULL == g_ValidHandles) { | |
52 g_ValidHandles = new(std::nothrow) std::set<const ScriptableHandle*>; | |
53 if (NULL == g_ValidHandles) { | |
54 return; | |
55 } | |
56 } | |
57 // Remember the handle. | |
58 g_ValidHandles->insert(handle); | |
59 } | |
60 | |
61 pp::Var Error(nacl::string call_name, const char* caller, | |
62 const char* error, pp::Var* exception) { | |
63 nacl::stringstream error_stream; | |
64 error_stream << call_name << ": " << error; | |
65 if (!exception->is_undefined()) { | |
66 error_stream << " - " + exception->AsString(); | |
67 } | |
68 // Get the error string in 2 steps; otherwise, the temporary string returned | |
69 // by the stream is destructed, causing a dangling pointer. | |
70 std::string str = error_stream.str(); | |
71 const char* e = str.c_str(); | |
72 PLUGIN_PRINTF(("ScriptableHandle::%s (%s)\n", caller, e)); | |
73 *exception = pp::Var(e); | |
74 return pp::Var(); | |
75 } | |
76 | |
77 // Helper functionality common to HasProperty and HasMethod. | |
78 bool HasCallType(Plugin* plugin, | |
79 CallType call_type, | |
80 nacl::string call_name, | |
81 const char* caller) { | |
82 uintptr_t id = plugin->browser_interface()->StringToIdentifier(call_name); | |
83 PLUGIN_PRINTF(("ScriptableHandle::%s (id=%"NACL_PRIxPTR")\n", | |
84 caller, id)); | |
85 return plugin->HasMethod(id, call_type); | |
86 } | |
87 | |
88 // Helper functionality common to GetProperty, SetProperty and Call. | |
89 // If |call_type| is PROPERTY_GET, ignores args and expects 1 return var. | |
90 // If |call_type| is PROPERTY_SET, expects 1 arg and returns void var. | |
91 // Sets |exception| on failure. | |
92 pp::Var Invoke(Plugin* plugin, | |
93 CallType call_type, | |
94 nacl::string call_name, | |
95 const char* caller, | |
96 const std::vector<pp::Var>& args, | |
97 pp::Var* exception) { | |
98 uintptr_t id = plugin->browser_interface()->StringToIdentifier(call_name); | |
99 | |
100 // Initialize input/output parameters. | |
101 SrpcParams params; | |
102 NaClSrpcArg** inputs = params.ins(); | |
103 NaClSrpcArg** outputs = params.outs(); | |
104 if (!plugin->InitParams(id, call_type, ¶ms)) { | |
105 return Error(call_name, caller, | |
106 "srpc parameter initialization failed", exception); | |
107 } | |
108 uint32_t input_length = params.InputLength(); | |
109 int32_t output_length = params.OutputLength(); | |
110 PLUGIN_PRINTF(("ScriptableHandle::%s (initialized %"NACL_PRIu32" ins, %" | |
111 NACL_PRIu32" outs)\n", caller, input_length, output_length)); | |
112 | |
113 // Verify input/output parameter list length. | |
114 if (args.size() != params.SignatureLength()) { | |
115 return Error(call_name, caller, | |
116 "incompatible srpc parameter list", exception); | |
117 } | |
118 PLUGIN_PRINTF(("ScriptableHandle::%s (verified signature)\n", caller)); | |
119 | |
120 // Marshall input parameters. | |
121 if (input_length > 0) { | |
122 assert(call_type != PROPERTY_GET); // expect no inputs for "get" | |
123 for (int i = 0; (i < NACL_SRPC_MAX_ARGS) && (inputs[i] != NULL); ++i) { | |
124 if (!PPVarToNaClSrpcArg(args[i], inputs[i], exception)) { | |
125 return Error(call_name, caller, | |
126 "srpc input marshalling failed", exception); | |
127 } | |
128 } | |
129 } | |
130 if (call_type == PROPERTY_SET) assert(input_length == 1); | |
131 PLUGIN_PRINTF(("ScriptableHandle::%s (marshalled inputs)\n", caller)); | |
132 | |
133 // Allocate array-typed output parameters. | |
134 if (args.size() > input_length) { | |
135 for (int i = 0; (i < NACL_SRPC_MAX_ARGS) && (outputs[i] != NULL); ++i) { | |
136 if (!PPVarToAllocateNaClSrpcArg(args[input_length + i], | |
137 outputs[i], exception)) { | |
138 return Error(call_name, caller, "srpc output array allocation failed", | |
139 exception); | |
140 } | |
141 } | |
142 } | |
143 PLUGIN_PRINTF(("ScriptableHandle::%s (output array allocation done)\n", | |
144 caller)); | |
145 | |
146 // Invoke. | |
147 if (!plugin->Invoke(id, call_type, ¶ms)) { | |
148 nacl::string err = nacl::string(caller) + "('" + call_name + "') failed\n"; | |
149 if (params.exception_string() != NULL) { | |
150 err = params.exception_string(); | |
151 } | |
152 *exception = pp::Var(err.c_str()); | |
153 return Error(call_name, caller, "invocation failed", exception); | |
154 } | |
155 PLUGIN_PRINTF(("ScriptableHandle::%s (invocation done)\n", caller)); | |
156 | |
157 // Marshall output parameters. | |
158 pp::Var retvar; | |
159 if (output_length > 0) { | |
160 assert(call_type != PROPERTY_SET); // expect no outputs for "set" | |
161 retvar = NaClSrpcArgToPPVar(outputs[0], plugin, exception); | |
162 if (output_length > 1) { | |
163 ArrayPpapi* array = new(std::nothrow) ArrayPpapi(plugin); | |
164 if (array == NULL) { | |
165 *exception = pp::Var("failed to allocate output array"); | |
166 } else { | |
167 array->SetProperty(pp::Var(0), retvar, exception); | |
168 for (int32_t i = 1; i < output_length; ++i) { | |
169 pp::Var v = NaClSrpcArgToPPVar(outputs[i], plugin, exception); | |
170 array->SetProperty(pp::Var(i), v, exception); | |
171 } | |
172 } | |
173 | |
174 retvar = pp::VarPrivate(plugin, array); | |
175 } | |
176 if (!exception->is_undefined()) { | |
177 return Error(call_name, caller, "srpc output marshalling failed", | |
178 exception); | |
179 } | |
180 } | |
181 if (call_type == PROPERTY_GET) assert(output_length == 1); | |
182 return retvar; | |
183 } | |
184 | |
185 } // namespace | |
186 | |
187 ScriptableHandle::ScriptableHandle(Plugin* plugin) | |
188 : var_(NULL), num_unref_calls_(0), plugin_(plugin), desc_handle_(NULL) { | |
189 PLUGIN_PRINTF(("ScriptableHandle::ScriptableHandle (this=%p, plugin=%p)\n", | |
190 static_cast<void*>(this), | |
191 static_cast<void*>(plugin))); | |
192 RememberValidHandle(this); | |
193 PLUGIN_PRINTF(("ScriptableHandle::ScriptableHandle (this=%p)\n", | |
194 static_cast<void*>(this))); | |
195 } | |
196 | |
197 ScriptableHandle::ScriptableHandle(DescBasedHandle* desc_handle) | |
198 : var_(NULL), num_unref_calls_(0), plugin_(NULL), desc_handle_(desc_handle) { | |
199 PLUGIN_PRINTF(("ScriptableHandle::ScriptableHandle (this=%p," | |
200 " desc_handle=%p)\n", | |
201 static_cast<void*>(this), | |
202 static_cast<void*>(desc_handle))); | |
203 RememberValidHandle(this); | |
204 PLUGIN_PRINTF(("ScriptableHandle::ScriptableHandle (this=%p)\n", | |
205 static_cast<void*>(this))); | |
206 } | |
207 | |
208 ScriptableHandle::~ScriptableHandle() { | |
209 PLUGIN_PRINTF(("ScriptableHandle::~ScriptableHandle (this=%p)\n", | |
210 static_cast<void*>(this))); | |
211 // If the set was empty, just return. | |
212 if (NULL == g_ValidHandles) { | |
213 return; | |
214 } | |
215 // Remove the scriptable handle from the set of valid handles. | |
216 g_ValidHandles->erase(this); | |
217 // If handle is a plugin, the browser is deleting it (and might have | |
218 // already done so). Otherwise, delete here. | |
219 if (desc_handle_ != NULL) { | |
220 PLUGIN_PRINTF(("ScriptableHandle::~ScriptableHandle " | |
221 "(this=%p, delete desc_handle=%p)\n", | |
222 static_cast<void*>(this), static_cast<void*>(desc_handle_))); | |
223 delete desc_handle_; | |
224 desc_handle_ = NULL; | |
225 } | |
226 PLUGIN_PRINTF(("ScriptableHandle::~ScriptableHandle (this=%p, return)\n", | |
227 static_cast<void*>(this))); | |
228 } | |
229 | |
230 // Check that an object is a validly created ScriptableHandle. | |
231 bool ScriptableHandle::is_valid(const ScriptableHandle* handle) { | |
232 PLUGIN_PRINTF(("ScriptableHandle::is_valid (handle=%p)\n", | |
233 static_cast<void*>(const_cast<ScriptableHandle*>(handle)))); | |
234 if (NULL == g_ValidHandles) { | |
235 PLUGIN_PRINTF(("ScriptableHandle::is_valid (return 0)\n")); | |
236 return false; | |
237 } | |
238 size_t count = | |
239 g_ValidHandles->count(static_cast<const ScriptableHandle*>(handle)); | |
240 PLUGIN_PRINTF(("ScriptableHandle::is_valid (handle=%p, count=%" | |
241 NACL_PRIuS")\n", | |
242 static_cast<void*>(const_cast<ScriptableHandle*>(handle)), | |
243 count)); | |
244 return 0 != count; | |
245 } | |
246 | |
247 void ScriptableHandle::Unref(ScriptableHandle** handle) { | |
248 if (*handle != NULL) { | |
249 (*handle)->Unref(); | |
250 *handle = NULL; | |
251 } | |
252 } | |
253 | |
254 ScriptableHandle* ScriptableHandle::NewPlugin(Plugin* plugin) { | |
255 PLUGIN_PRINTF(("ScriptableHandle::NewPlugin (plugin=%p)\n", | |
256 static_cast<void*>(plugin))); | |
257 if (plugin == NULL) { | |
258 return NULL; | |
259 } | |
260 ScriptableHandle* scriptable_handle = | |
261 new(std::nothrow) ScriptableHandle(plugin); | |
262 if (scriptable_handle == NULL) { | |
263 return NULL; | |
264 } | |
265 PLUGIN_PRINTF(("ScriptableHandle::NewPlugin (return %p)\n", | |
266 static_cast<void*>(scriptable_handle))); | |
267 return scriptable_handle; | |
268 } | |
269 | |
270 | |
271 ScriptableHandle* ScriptableHandle::NewDescHandle( | |
272 DescBasedHandle* desc_handle) { | |
273 PLUGIN_PRINTF(("ScriptableHandle::NewDescHandle (desc_handle=%p)\n", | |
274 static_cast<void*>(desc_handle))); | |
275 if (desc_handle == NULL) { | |
276 return NULL; | |
277 } | |
278 ScriptableHandle* scriptable_handle = | |
279 new(std::nothrow) ScriptableHandle(desc_handle); | |
280 if (scriptable_handle == NULL) { | |
281 return NULL; | |
282 } | |
283 PLUGIN_PRINTF(("ScriptableHandle::NewDescHandle (return %p)\n", | |
284 static_cast<void*>(scriptable_handle))); | |
285 return scriptable_handle; | |
286 } | |
287 | |
288 | |
289 bool ScriptableHandle::HasProperty(const pp::Var& name, pp::Var* exception) { | |
290 UNREFERENCED_PARAMETER(exception); | |
291 PLUGIN_PRINTF(("ScriptableHandle::HasProperty (this=%p, name=%s)\n", | |
292 static_cast<void*>(this), name.DebugString().c_str())); | |
293 if (plugin_ == NULL) { | |
294 return false; | |
295 } | |
296 if (!name.is_string() && !name.is_int()) | |
297 return false; | |
298 bool has_property = HasCallType(plugin_, | |
299 PROPERTY_GET, | |
300 name.AsString(), | |
301 "HasProperty"); | |
302 PLUGIN_PRINTF(("ScriptableHandle::HasProperty (has_property=%d)\n", | |
303 has_property)); | |
304 return has_property; | |
305 } | |
306 | |
307 | |
308 bool ScriptableHandle::HasMethod(const pp::Var& name, pp::Var* exception) { | |
309 UNREFERENCED_PARAMETER(exception); | |
310 PLUGIN_PRINTF(("ScriptableHandle::HasMethod (this=%p, name='%s')\n", | |
311 static_cast<void*>(this), name.DebugString().c_str())); | |
312 if (plugin_ == NULL) { | |
313 return false; | |
314 } | |
315 if (!name.is_string()) | |
316 return false; | |
317 bool has_method = HasCallType(plugin_, | |
318 METHOD_CALL, | |
319 name.AsString(), | |
320 "HasMethod"); | |
321 PLUGIN_PRINTF(("ScriptableHandle::HasMethod (has_method=%d)\n", | |
322 has_method)); | |
323 return has_method; | |
324 } | |
325 | |
326 | |
327 pp::Var ScriptableHandle::GetProperty(const pp::Var& name, | |
328 pp::Var* exception) { | |
329 PLUGIN_PRINTF(("ScriptableHandle::GetProperty (name=%s)\n", | |
330 name.DebugString().c_str())); | |
331 if (plugin_ == NULL) { | |
332 return pp::Var(); | |
333 } | |
334 pp::Var property = Invoke(plugin_, | |
335 PROPERTY_GET, | |
336 NameAsString(name), | |
337 "GetProperty", | |
338 std::vector<pp::Var>(), exception); | |
339 PLUGIN_PRINTF(("ScriptableHandle::GetProperty (property=%s)\n", | |
340 property.DebugString().c_str())); | |
341 return property; | |
342 } | |
343 | |
344 | |
345 void ScriptableHandle::SetProperty(const pp::Var& name, | |
346 const pp::Var& value, | |
347 pp::Var* exception) { | |
348 PLUGIN_PRINTF(("ScriptableHandle::SetProperty (name=%s, value=%s)\n", | |
349 name.DebugString().c_str(), value.DebugString().c_str())); | |
350 if (plugin_ == NULL) { | |
351 return; | |
352 } | |
353 std::vector<pp::Var> args; | |
354 args.push_back(pp::Var(pp::Var::DontManage(), value.pp_var())); | |
355 Invoke(plugin_, | |
356 PROPERTY_SET, | |
357 NameAsString(name), | |
358 "SetProperty", | |
359 args, | |
360 exception); | |
361 std::string exception_string("NULL"); | |
362 if (exception != NULL) { | |
363 exception_string = exception->DebugString(); | |
364 } | |
365 PLUGIN_PRINTF(("ScriptableHandle::SetProperty (exception=%s)\n", | |
366 exception_string.c_str())); | |
367 } | |
368 | |
369 | |
370 void ScriptableHandle::RemoveProperty(const pp::Var& name, | |
371 pp::Var* exception) { | |
372 PLUGIN_PRINTF(("ScriptableHandle::RemoveProperty (name=%s)\n", | |
373 name.DebugString().c_str())); | |
374 Error(NameAsString(name), "RemoveProperty", | |
375 "property removal is not supported", exception); | |
376 } | |
377 | |
378 // TODO(polina): should methods also be added? | |
379 // This is currently never called and the exact semantics is not clear. | |
380 // http://code.google.com/p/chromium/issues/detail?id=51089 | |
381 void ScriptableHandle::GetAllPropertyNames(std::vector<pp::Var>* properties, | |
382 pp::Var* exception) { | |
383 UNREFERENCED_PARAMETER(exception); | |
384 PLUGIN_PRINTF(("ScriptableHandle::GetAllPropertyNames ()\n")); | |
385 if (plugin_ == NULL) { | |
386 return; | |
387 } | |
388 std::vector<uintptr_t>* ids = plugin_->GetPropertyIdentifiers(); | |
389 if (ids == NULL) { | |
390 PLUGIN_PRINTF(("ScriptableHandle::GetAllPropertyNames " | |
391 "(ids=%p)\n", reinterpret_cast<void*>(ids))); | |
392 return; | |
393 } | |
394 PLUGIN_PRINTF(("ScriptableHandle::GetAllPropertyNames " | |
395 "(ids->size()=%"NACL_PRIuS")\n", ids->size())); | |
396 for (size_t i = 0; i < ids->size(); ++i) { | |
397 nacl::string name = | |
398 plugin_->browser_interface()->IdentifierToString(ids->at(i)); | |
399 properties->push_back(pp::Var(name)); | |
400 } | |
401 PLUGIN_PRINTF(("ScriptableHandle::GetAllPropertyNames " | |
402 "(properties=%"NACL_PRIuS")\n", properties->size())); | |
403 } | |
404 | |
405 | |
406 pp::Var ScriptableHandle::Call(const pp::Var& name, | |
407 const std::vector<pp::Var>& args, | |
408 pp::Var* exception) { | |
409 PLUGIN_PRINTF(("ScriptableHandle::Call (name=%s, %"NACL_PRIuS | |
410 " args)\n", name.DebugString().c_str(), args.size())); | |
411 if (plugin_ == NULL) { | |
412 pp::Var(); | |
413 } | |
414 if (name.is_undefined()) // invoke default | |
415 return pp::Var(); | |
416 assert(name.is_string()); | |
417 pp::Var return_var = Invoke(plugin_, | |
418 METHOD_CALL, | |
419 name.AsString(), | |
420 "Call", | |
421 args, | |
422 exception); | |
423 PLUGIN_PRINTF(("ScriptableHandle::Call (return=%s)\n", | |
424 return_var.DebugString().c_str())); | |
425 return return_var; | |
426 } | |
427 | |
428 | |
429 pp::Var ScriptableHandle::Construct(const std::vector<pp::Var>& args, | |
430 pp::Var* exception) { | |
431 PLUGIN_PRINTF(("ScriptableHandle::Construct (%"NACL_PRIuS | |
432 " args)\n", args.size())); | |
433 return Error("constructor", "Construct", "constructor is not supported", | |
434 exception); | |
435 } | |
436 | |
437 | |
438 ScriptableHandle* ScriptableHandle::AddRef() { | |
439 // This is called when we are about to share this object with the browser, | |
440 // and we need to make sure we have an internal plugin reference, so this | |
441 // object doesn't get deallocated when the browser discards its references. | |
442 if (var_ == NULL) { | |
443 var_ = new(std::nothrow) pp::VarPrivate(plugin_, this); | |
444 CHECK(var_ != NULL); | |
445 } | |
446 PLUGIN_PRINTF(("ScriptableHandle::AddRef (this=%p, var=%p)\n", | |
447 static_cast<void*>(this), static_cast<void*>(var_))); | |
448 return this; | |
449 } | |
450 | |
451 | |
452 void ScriptableHandle::Unref() { | |
453 // We should have no more than one internal owner of this object, so this | |
454 // should be called no more than once. | |
455 CHECK(++num_unref_calls_ == 1); | |
456 PLUGIN_PRINTF(("ScriptableHandle::Unref (this=%p, var=%p)\n", | |
457 static_cast<void*>(this), static_cast<void*>(var_))); | |
458 if (var_ != NULL) { | |
459 // We have shared this with the browser while keeping our own var | |
460 // reference, but we no longer need ours. If the browser has copies, | |
461 // it will clean things up later, otherwise this object will get | |
462 // deallocated right away. | |
463 PLUGIN_PRINTF(("ScriptableHandle::Unref (delete var)\n")); | |
464 pp::Var* var = var_; | |
465 var_ = NULL; | |
466 delete var; | |
467 } else { | |
468 // Neither the browser nor plugin ever var referenced this object, | |
469 // so it can safely discarded. | |
470 PLUGIN_PRINTF(("ScriptableHandle::Unref (delete this)\n")); | |
471 CHECK(var_ == NULL); | |
472 delete this; | |
473 } | |
474 } | |
475 | |
476 | |
477 } // namespace plugin | |
OLD | NEW |