blob: 91bcaafb805b9ddee1bb06573a165b4e6c591055 [file] [log] [blame]
jat@google.com134be542009-08-03 15:30:11 +00001#ifndef _H_NPVariantWrapper
2#define _H_NPVariantWrapper
3
4#include <string>
5#include <ostream>
6
7#ifdef sun
8// Sun's cstring doesn't define strlen/etc
9#include <string.h>
10#endif
11#include <cstring>
12#include <stdio.h>
13
14#include "Debug.h"
15#include "Platform.h"
16
17#include "mozincludes.h"
18
19#include "Value.h"
20#include "LocalObjectTable.h"
21#include "Plugin.h"
22#include "JavaObject.h"
23
24/**
25 * Contains NPVariantProxy, NPVariantWrapper, and NPVariantArray
26 */
27
28/**
29 * Wraps an NPVariant and provides various conversion functions. The variant
30 * provided retained at create time.
31 */
32class NPVariantProxy {
33 friend class NPVariantArray;
34private:
35 ScriptableInstance& plugin;
36 NPVariant& variant;
37public:
38 NPVariantProxy(ScriptableInstance& plugin, NPVariant& variant)
39 : plugin(plugin), variant(variant)
40 {
41 VOID_TO_NPVARIANT(variant);
42 }
43
44 NPVariantProxy(ScriptableInstance& plugin, NPVariant& variant, bool noinit)
45 : plugin(plugin), variant(variant)
46 {
47 }
48
49 ~NPVariantProxy() {
50 }
51
52 operator NPVariant() const {
53 return variant;
54 }
55
56 const NPVariant* operator->() const {
57 return &variant;
58 }
59
60 const NPVariant* address() const {
61 return &variant;
62 }
63
64 int isInt() const {
65 return isInt(variant);
66 }
67
68 static int isInt(const NPVariant& variant) {
69 return NPVARIANT_IS_INT32(variant);
70 }
71
72 int getAsInt() const {
73 return getAsInt(variant);
74 }
75
76 static int getAsInt(const NPVariant& variant) {
77 if (NPVARIANT_IS_INT32(variant)) {
78 return NPVARIANT_TO_INT32(variant);
79 }
80 Debug::log(Debug::Error) << "getAsInt: variant not int" << Debug::flush;
81 return 0;
82 }
83
84 int isNull() const {
85 return isNull(variant);
86 }
87
88 static int isNull(const NPVariant& variant) {
89 return NPVARIANT_IS_NULL(variant);
90 }
91
92 int isObject() const {
93 return isObject(variant);
94 }
95
96 static int isObject(const NPVariant& variant) {
97 return NPVARIANT_IS_OBJECT(variant);
98 }
99
100 NPObject* getAsObject() const {
101 return getAsObject(variant);
102 }
103
104 static NPObject* getAsObject(const NPVariant& variant) {
105 if (NPVARIANT_IS_OBJECT(variant)) {
106 return NPVARIANT_TO_OBJECT(variant);
107 }
108 Debug::log(Debug::Error) << "getAsObject: variant not object" << Debug::flush;
109 return 0;
110 }
111
112 int isString() const {
113 return isString(variant);
114 }
115
116 static int isString(const NPVariant& variant) {
117 return NPVARIANT_IS_STRING(variant);
118 }
119
120 const NPString* getAsNPString() const {
121 return getAsNPString(variant);
122 }
123
124 static const NPString* getAsNPString(const NPVariant& variant) {
125 if (NPVARIANT_IS_STRING(variant)) {
126 return &NPVARIANT_TO_STRING(variant);
127 }
128 Debug::log(Debug::Error) << "getAsNPString: variant not string" << Debug::flush;
129 return 0;
130 }
131
132 Value getAsValue(ScriptableInstance& scriptInstance, bool unwrapJava = true) const {
133 return getAsValue(variant, scriptInstance, unwrapJava);
134 }
135
136 static Value getAsValue(const NPVariant& variant, ScriptableInstance& scriptInstance,
137 bool unwrapJava = true) {
138 Value val;
139 if (NPVARIANT_IS_VOID(variant)) {
140 val.setUndefined();
141 } else if (NPVARIANT_IS_NULL(variant)) {
142 val.setNull();
143 } else if (NPVARIANT_IS_BOOLEAN(variant)) {
144 val.setBoolean(NPVARIANT_TO_BOOLEAN(variant));
145 } else if (NPVARIANT_IS_INT32(variant)) {
146 val.setInt(NPVARIANT_TO_INT32(variant));
147 } else if (NPVARIANT_IS_DOUBLE(variant)) {
148 val.setDouble(NPVARIANT_TO_DOUBLE(variant));
149 } else if (NPVARIANT_IS_STRING(variant)) {
150 NPString str = NPVARIANT_TO_STRING(variant);
151 val.setString(GetNPStringUTF8Characters(str), GetNPStringUTF8Length(str));
152 } else if (NPVARIANT_IS_OBJECT(variant)) {
153 NPObject* obj = NPVARIANT_TO_OBJECT(variant);
154 if (unwrapJava && JavaObject::isInstance(obj)) {
155 JavaObject* jObj = static_cast<JavaObject*>(obj);
156 val.setJavaObject(jObj->getObjectId());
157 } else {
158 NPVariant result;
159 VOID_TO_NPVARIANT(result);
160 if (scriptInstance.tryGetStringPrimitive(obj, result)) {
161 NPString str = NPVARIANT_TO_STRING(result);
162 val.setString(GetNPStringUTF8Characters(str), GetNPStringUTF8Length(str));
163 release(result);
164 } else {
165 val.setJsObjectId(scriptInstance.getLocalObjectRef(obj));
166 }
167 }
168 } else {
169 Debug::log(Debug::Error) << "Unsupported NPVariant type " << variant.type << Debug::flush;
170 }
171 return val;
172 }
173
174 /**
175 * The incoming variant is not altered, and is not even required to have
176 * its contents retained. Any object will get an extra refcount on it
177 * when copied to this variant, and strings will be copied.
178 */
179 NPVariantProxy& operator=(const NPVariant& newval) {
180 assignFrom(variant, newval);
181 return *this;
182 }
183
184 /**
185 * The incoming variant is not altered, and is not even required to have
186 * its contents retained. Any object will get an extra refcount on it
187 * when copied to this variant, and strings will be copied.
188 */
189 static void assignFrom(NPVariant& variant, const NPVariant& newval) {
190 release(variant);
191 variant = newval;
192 if (NPVARIANT_IS_STRING(newval)) {
193 int n = variant.value.stringValue.UTF8Length;
194 char* strBytes = reinterpret_cast<char*>(NPN_MemAlloc(n));
195 memcpy(strBytes, variant.value.stringValue.UTF8Characters, n);
196 variant.value.stringValue.UTF8Characters = strBytes;
197 } else {
198 retain(variant);
199 }
200 }
201
202 NPVariantProxy& operator=(NPObject* obj) {
203 assignFrom(variant, obj);
204 return *this;
205 }
206
207 static void assignFrom(NPVariant& variant, NPObject* obj) {
208 release(variant);
209 OBJECT_TO_NPVARIANT(obj, variant);
210 retain(variant);
211 }
212
213 // Convenience method for C++ code
214 NPVariantProxy& operator=(int intVal) {
215 assignFrom(variant, intVal);
216 return *this;
217 }
218
219 // Convenience method for C++ code
220 static void assignFrom(NPVariant& variant, int intVal) {
221 NPVariant newvar;
222 INT32_TO_NPVARIANT(intVal, newvar);
223 assignFrom(variant, newvar);
224 }
225
226 // Convenience method for C++ code
227 NPVariantProxy& operator=(const std::string& strval) {
228 assignFrom(variant, strval);
229 return *this;
230 }
231
232 // Convenience method for C++ code
233 static void assignFrom(NPVariant& variant, const std::string& strval) {
234 NPVariant newvar;
235 STDSTRING_TO_NPVARIANT(strval, newvar);
236 assignFrom(variant, newvar);
237 }
238
239 // Convenience method for C++ code
240 NPVariantProxy& operator=(const char* strval) {
241 assignFrom(variant, strval);
242 return *this;
243 }
244
245 // Convenience method for C++ code
246 static void assignFrom(NPVariant& variant, const char* strval) {
247 NPVariant newvar;
248 STRINGZ_TO_NPVARIANT(strval, newvar);
249 assignFrom(variant, newvar);
250 }
251
252 NPVariantProxy& operator=(const Value& newval) {
253 assignFrom(plugin, variant, newval);
254 return *this;
255 }
256
257 static void assignFrom(ScriptableInstance& plugin, NPVariant& variant, const Value& newval) {
258 NPVariant newvar;
259 VOID_TO_NPVARIANT(newvar);
260 if (newval.isBoolean()) {
261 BOOLEAN_TO_NPVARIANT(newval.getBoolean(), newvar);
262 } else if (newval.isByte()) {
263 INT32_TO_NPVARIANT(newval.getByte(), newvar);
264 } else if (newval.isChar()) {
265 INT32_TO_NPVARIANT(newval.getChar(), newvar);
266 } else if (newval.isShort()) {
267 INT32_TO_NPVARIANT(newval.getShort(), newvar);
268 } else if (newval.isInt()) {
269 int value = newval.getInt();
270 // Firefox NPAPI bug: 32-bit ints get mapped to int jsvals, regardless of range.
271 // However, int jsvals are 31 bits, so we need to use a double if the value is
272 // not representable in a 31 bit signed 2's-complement value.
273 if (value >= 0x40000000 || value < -0x40000000) {
274 DOUBLE_TO_NPVARIANT(static_cast<double>(value), newvar);
275 } else {
276 INT32_TO_NPVARIANT(value, newvar);
277 }
278 } else if (newval.isFloat()) {
279 DOUBLE_TO_NPVARIANT(newval.getFloat(), newvar);
280 } else if (newval.isDouble()) {
281 DOUBLE_TO_NPVARIANT(newval.getDouble(), newvar);
282 } else if (newval.isNull()) {
283 NULL_TO_NPVARIANT(newvar);
284 } else if (newval.isUndefined()) {
285 VOID_TO_NPVARIANT(newvar);
286 } else if (newval.isString()) {
287 assignFrom(variant, newval.getString());
288 return;
289 } else if (newval.isJavaObject()) {
290 if (1) {
291 JavaObject* jObj = plugin.createJavaWrapper(newval.getJavaObjectId());
292 NPObject* obj = jObj;
293 OBJECT_TO_NPVARIANT(obj, newvar);
294 } else {
295 VOID_TO_NPVARIANT(newvar);
296 }
297 } else if (newval.isJsObject()) {
298 OBJECT_TO_NPVARIANT(plugin.getLocalObject(newval.getJsObjectId()),
299 newvar);
300 } else {
301 Debug::log(Debug::Error) << "Unsupported NPVariant type " << newval.getType() << Debug::flush;
302 }
303 assignFrom(variant, newvar);
304 }
305
306 std::string toString() const {
307 return toString(variant);
308 }
309
310 static std::string toString(const NPVariant& variant) {
311 std::string retval;
312 // TODO(jat): remove sprintfs
313 char buf[40];
314 NPObject* npObj;
315 switch (variant.type) {
316 case NPVariantType_Void:
317 retval = "undef";
318 break;
319 case NPVariantType_Null:
320 retval = "null";
321 break;
322 case NPVariantType_Bool:
323 retval = "bool(";
324 retval += (NPVARIANT_TO_BOOLEAN(variant) ? "true" : "false");
325 retval += ')';
326 break;
327 case NPVariantType_Int32:
328 retval = "int(";
329 snprintf(buf, sizeof(buf), "%d)", NPVARIANT_TO_INT32(variant));
330 retval += buf;
331 break;
332 case NPVariantType_Double:
333 retval = "double(";
334 snprintf(buf, sizeof(buf), "%g)", NPVARIANT_TO_DOUBLE(variant));
335 retval += buf;
336 break;
337 case NPVariantType_String:
338 {
339 retval = "string(";
340 NPString str = NPVARIANT_TO_STRING(variant);
341 retval += std::string(str.UTF8Characters, str.UTF8Length);
342 retval += ')';
343 }
344 break;
345 case NPVariantType_Object:
346 npObj = NPVARIANT_TO_OBJECT(variant);
347 if (JavaObject::isInstance(npObj)) {
348 JavaObject* javaObj = static_cast<JavaObject*>(npObj);
349 snprintf(buf, sizeof(buf), "javaObj(id=%d, ", javaObj->getObjectId());
350 } else {
351 snprintf(buf, sizeof(buf), "jsObj(class=%p, ", npObj->_class);
352 }
353 retval = buf;
354 snprintf(buf, sizeof(buf), "%p)", npObj);
355 retval += buf;
356 break;
357 default:
358 snprintf(buf, sizeof(buf), "Unknown type %d", variant.type);
359 retval = buf;
360 break;
361 }
362 return retval;
363 }
364
365public:
366 void release() {
367 release(variant);
368 }
369
370 static void release(NPVariant& variant) {
371 NPN_ReleaseVariantValue(&variant);
372 }
373
374 void retain() {
375 retain(variant);
376 }
377
378 static void retain(NPVariant& variant) {
379 if (NPVARIANT_IS_OBJECT(variant)) {
380 NPN_RetainObject(NPVARIANT_TO_OBJECT(variant));
381 }
382 }
383};
384
385inline Debug::DebugStream& operator<<(Debug::DebugStream& dbg, const NPVariant& var) {
386 return dbg << NPVariantProxy::toString(var);
387}
388
389inline Debug::DebugStream& operator<<(Debug::DebugStream& dbg, const NPVariantProxy& var) {
390 return dbg << var.toString();
391}
392
393/**
394 * Variation of NPVariantProxy that provides its own variant and always frees it
395 * when the wrapper goes away.
396 */
397class NPVariantWrapper {
398private:
399 ScriptableInstance& plugin;
400 NPVariant variant;
401public:
402 NPVariantWrapper(ScriptableInstance& plugin) : plugin(plugin) {
403 VOID_TO_NPVARIANT(variant);
404 }
405
406 ~NPVariantWrapper() {
407 release();
408 }
409
410 operator NPVariant() const {
411 return variant;
412 }
413
414 const NPVariant* operator->() const {
415 return &variant;
416 }
417
418 const NPVariant* address() const {
419 return &variant;
420 }
421
422 /**
423 * Get the address for use as a return value. Since the value can be trashed,
424 * we need to release any data we currently hold.
425 */
426 NPVariant* addressForReturn() {
427 NPVariantProxy::release(variant);
428 VOID_TO_NPVARIANT(variant);
429 NPVariantProxy::retain(variant); // does nothing, present for consistency
430 return &variant;
431 }
432
433 int isInt() const {
434 return NPVariantProxy::isInt(variant);
435 }
436
437 int isObject() const {
438 return NPVariantProxy::isObject(variant);
439 }
440
441 int isString() const {
442 return NPVariantProxy::isString(variant);
443 }
444
445 int getAsInt() const {
446 return NPVariantProxy::getAsInt(variant);
447 }
448
449 NPObject* getAsObject() const {
450 return NPVariantProxy::getAsObject(variant);
451 }
452
453 const NPString* getAsNPString() const {
454 return NPVariantProxy::getAsNPString(variant);
455 }
456
457 Value getAsValue(ScriptableInstance& scriptInstance, bool unwrapJava = true) const {
458 return NPVariantProxy::getAsValue(variant, scriptInstance, unwrapJava);
459 }
460
461 /**
462 * The incoming variant is not altered, and is not even required to have
463 * its contents retained. Any object will get an extra refcount on it
464 * when copied to this variant, and strings will be copied.
465 */
466 NPVariantWrapper& operator=(const NPVariant& newval) {
467 NPVariantProxy::assignFrom(variant, newval);
468 return *this;
469 }
470
471 NPVariantWrapper& operator=(const Value& newval) {
472 NPVariantProxy::assignFrom(plugin, variant, newval);
473 return *this;
474 }
475
476 NPVariantWrapper& operator=(NPObject* obj) {
477 NPVariantProxy::assignFrom(variant, obj);
478 return *this;
479 }
480
481 // Convenience method for C++ code
482 NPVariantWrapper& operator=(const std::string& strval) {
483 NPVariantProxy::assignFrom(variant, strval);
484 return *this;
485 }
486
487 // Convenience method for C++ code
488 NPVariantWrapper& operator=(const char* strval) {
489 NPVariantProxy::assignFrom(variant, strval);
490 return *this;
491 }
492
jat@google.comf78175f2009-09-09 21:57:53 +0000493 // Convenience method for C++ code
494 NPVariantWrapper& operator=(int intval) {
495 NPVariantProxy::assignFrom(variant, intval);
496 return *this;
497 }
498
jat@google.com134be542009-08-03 15:30:11 +0000499 void release() {
500 NPVariantProxy::release(variant);
501 }
502
503 void retain() {
504 NPVariantProxy::retain(variant);
505 }
506
507 std::string toString() const {
508 return NPVariantProxy::toString(variant);
509 }
510};
511
512inline Debug::DebugStream& operator<<(Debug::DebugStream& dbg, const NPVariantWrapper& var) {
513 dbg << var.toString();
514 return dbg;
515}
516
517/**
518 * Maintains an array of NPVariants and cleans them up when it is destroyed.
519 */
520class NPVariantArray {
521private:
522 ScriptableInstance& plugin;
523 int size;
524 NPVariant* args;
525
526public:
527 NPVariantArray(ScriptableInstance& plugin, int size) : plugin(plugin), size(size) {
528 args = new NPVariant[size];
529 for (int i = 0; i < size; ++i) {
530 VOID_TO_NPVARIANT(args[i]);
531 }
532 }
533
534 ~NPVariantArray() {
535 for (int i = 0; i < size; ++i) {
536 NPN_ReleaseVariantValue(&args[i]);
537 }
538 delete [] args;
539 }
540
541 const NPVariant* getArray() const {
542 return args;
543 }
544
545 int getSize() const {
546 return size;
547 }
548
549 const NPVariant& operator[](int idx) const {
550 if (idx >= size) {
551 printf("NPVariantArray[idx=%d] const: size=%d\n", idx, size);
552 }
553 return args[idx];
554 }
555
556 NPVariantProxy operator[](int idx) {
557 if (idx >= size) {
558 printf("NPVariantArray[idx=%d]: size=%d\n", idx, size);
559 }
560 return NPVariantProxy(plugin, args[idx], true);
561 }
562};
563
564inline Debug::DebugStream& operator<<(Debug::DebugStream& dbg, const NPVariantArray& var) {
565 dbg << "[";
566 for (int i = 0; i < var.getSize(); ++i) {
567 dbg << " " << var[i];
568 }
569 dbg << " ]";
570 return dbg;
571}
572
573#endif