|  | #ifndef _H_NPVariantWrapper | 
|  | #define _H_NPVariantWrapper | 
|  |  | 
|  | #include <string> | 
|  | #include <ostream> | 
|  |  | 
|  | #ifdef sun | 
|  | // Sun's cstring doesn't define strlen/etc | 
|  | #include <string.h> | 
|  | #endif | 
|  | #include <cstring> | 
|  | #include <stdio.h> | 
|  |  | 
|  | #include "Debug.h" | 
|  | #include "Platform.h" | 
|  |  | 
|  | #include "mozincludes.h" | 
|  |  | 
|  | #include "Value.h" | 
|  | #include "LocalObjectTable.h" | 
|  | #include "Plugin.h" | 
|  | #include "JavaObject.h" | 
|  |  | 
|  | /** | 
|  | * Contains NPVariantProxy, NPVariantWrapper, and NPVariantArray | 
|  | */ | 
|  |  | 
|  | /** | 
|  | * Wraps an NPVariant and provides various conversion functions.  The variant | 
|  | * provided retained at create time. | 
|  | */ | 
|  | class NPVariantProxy { | 
|  | friend class NPVariantArray; | 
|  | private: | 
|  | ScriptableInstance& plugin; | 
|  | NPVariant& variant; | 
|  | public: | 
|  | NPVariantProxy(ScriptableInstance& plugin, NPVariant& variant) | 
|  | : plugin(plugin), variant(variant) | 
|  | { | 
|  | VOID_TO_NPVARIANT(variant); | 
|  | } | 
|  |  | 
|  | NPVariantProxy(ScriptableInstance& plugin, NPVariant& variant, bool noinit) | 
|  | : plugin(plugin), variant(variant) | 
|  | { | 
|  | } | 
|  |  | 
|  | ~NPVariantProxy() { | 
|  | } | 
|  |  | 
|  | operator NPVariant() const { | 
|  | return variant; | 
|  | } | 
|  |  | 
|  | const NPVariant* operator->() const { | 
|  | return &variant; | 
|  | } | 
|  |  | 
|  | const NPVariant* address() const { | 
|  | return &variant; | 
|  | } | 
|  |  | 
|  | int isInt() const { | 
|  | return isInt(variant); | 
|  | } | 
|  |  | 
|  | static int isInt(const NPVariant& variant) { | 
|  | return NPVARIANT_IS_INT32(variant); | 
|  | } | 
|  |  | 
|  | int getAsInt() const { | 
|  | return getAsInt(variant); | 
|  | } | 
|  |  | 
|  | static int getAsInt(const NPVariant& variant) { | 
|  | if (NPVARIANT_IS_INT32(variant)) { | 
|  | return NPVARIANT_TO_INT32(variant); | 
|  | } | 
|  | Debug::log(Debug::Error) << "getAsInt: variant not int" << Debug::flush; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int isNull() const { | 
|  | return isNull(variant); | 
|  | } | 
|  |  | 
|  | static int isNull(const NPVariant& variant) { | 
|  | return NPVARIANT_IS_NULL(variant); | 
|  | } | 
|  |  | 
|  | int isObject() const { | 
|  | return isObject(variant); | 
|  | } | 
|  |  | 
|  | static int isObject(const NPVariant& variant) { | 
|  | return NPVARIANT_IS_OBJECT(variant); | 
|  | } | 
|  |  | 
|  | NPObject* getAsObject() const { | 
|  | return getAsObject(variant); | 
|  | } | 
|  |  | 
|  | static NPObject* getAsObject(const NPVariant& variant) { | 
|  | if (NPVARIANT_IS_OBJECT(variant)) { | 
|  | return NPVARIANT_TO_OBJECT(variant); | 
|  | } | 
|  | Debug::log(Debug::Error) << "getAsObject: variant not object" << Debug::flush; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int isString() const { | 
|  | return isString(variant); | 
|  | } | 
|  |  | 
|  | static int isString(const NPVariant& variant) { | 
|  | return NPVARIANT_IS_STRING(variant); | 
|  | } | 
|  |  | 
|  | const NPString* getAsNPString() const { | 
|  | return getAsNPString(variant); | 
|  | } | 
|  |  | 
|  | static const NPString* getAsNPString(const NPVariant& variant) { | 
|  | if (NPVARIANT_IS_STRING(variant)) { | 
|  | return &NPVARIANT_TO_STRING(variant); | 
|  | } | 
|  | Debug::log(Debug::Error) << "getAsNPString: variant not string" << Debug::flush; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | Value getAsValue(ScriptableInstance& scriptInstance, bool unwrapJava = true) const { | 
|  | return getAsValue(variant, scriptInstance, unwrapJava); | 
|  | } | 
|  |  | 
|  | static Value getAsValue(const NPVariant& variant, ScriptableInstance& scriptInstance, | 
|  | bool unwrapJava = true) { | 
|  | Value val; | 
|  | if (NPVARIANT_IS_VOID(variant)) { | 
|  | val.setUndefined(); | 
|  | } else if (NPVARIANT_IS_NULL(variant)) { | 
|  | val.setNull(); | 
|  | } else if (NPVARIANT_IS_BOOLEAN(variant)) { | 
|  | val.setBoolean(NPVARIANT_TO_BOOLEAN(variant)); | 
|  | } else if (NPVARIANT_IS_INT32(variant)) { | 
|  | val.setInt(NPVARIANT_TO_INT32(variant)); | 
|  | } else if (NPVARIANT_IS_DOUBLE(variant)) { | 
|  | val.setDouble(NPVARIANT_TO_DOUBLE(variant)); | 
|  | } else if (NPVARIANT_IS_STRING(variant)) { | 
|  | NPString str = NPVARIANT_TO_STRING(variant); | 
|  | val.setString(GetNPStringUTF8Characters(str), GetNPStringUTF8Length(str)); | 
|  | } else if (NPVARIANT_IS_OBJECT(variant)) { | 
|  | NPObject* obj = NPVARIANT_TO_OBJECT(variant); | 
|  | if (unwrapJava && JavaObject::isInstance(obj)) { | 
|  | JavaObject* jObj = static_cast<JavaObject*>(obj); | 
|  | val.setJavaObject(jObj->getObjectId()); | 
|  | } else { | 
|  | NPVariant result; | 
|  | VOID_TO_NPVARIANT(result); | 
|  | if (scriptInstance.tryGetStringPrimitive(obj, result)) { | 
|  | NPString str = NPVARIANT_TO_STRING(result); | 
|  | val.setString(GetNPStringUTF8Characters(str), GetNPStringUTF8Length(str)); | 
|  | release(result); | 
|  | } else { | 
|  | val.setJsObjectId(scriptInstance.getLocalObjectRef(obj)); | 
|  | } | 
|  | } | 
|  | } else { | 
|  | Debug::log(Debug::Error) << "Unsupported NPVariant type " << variant.type << Debug::flush; | 
|  | } | 
|  | return val; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * The incoming variant is not altered, and is not even required to have | 
|  | * its contents retained.  Any object will get an extra refcount on it | 
|  | * when copied to this variant, and strings will be copied. | 
|  | */ | 
|  | NPVariantProxy& operator=(const NPVariant& newval) { | 
|  | assignFrom(variant, newval); | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * The incoming variant is not altered, and is not even required to have | 
|  | * its contents retained.  Any object will get an extra refcount on it | 
|  | * when copied to this variant, and strings will be copied. | 
|  | */ | 
|  | static void assignFrom(NPVariant& variant, const NPVariant& newval) { | 
|  | release(variant); | 
|  | variant = newval; | 
|  | if (NPVARIANT_IS_STRING(newval)) { | 
|  | int n = variant.value.stringValue.UTF8Length; | 
|  | char* strBytes = reinterpret_cast<char*>(NPN_MemAlloc(n)); | 
|  | memcpy(strBytes, variant.value.stringValue.UTF8Characters, n); | 
|  | variant.value.stringValue.UTF8Characters = strBytes; | 
|  | } else { | 
|  | retain(variant); | 
|  | } | 
|  | } | 
|  |  | 
|  | NPVariantProxy& operator=(NPObject* obj) { | 
|  | assignFrom(variant, obj); | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | static void assignFrom(NPVariant& variant, NPObject* obj) { | 
|  | release(variant); | 
|  | OBJECT_TO_NPVARIANT(obj, variant); | 
|  | retain(variant); | 
|  | } | 
|  |  | 
|  | // Convenience method for C++ code | 
|  | NPVariantProxy& operator=(int intVal) { | 
|  | assignFrom(variant, intVal); | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | // Convenience method for C++ code | 
|  | static void assignFrom(NPVariant& variant, int intVal) { | 
|  | NPVariant newvar; | 
|  | INT32_TO_NPVARIANT(intVal, newvar); | 
|  | assignFrom(variant, newvar); | 
|  | } | 
|  |  | 
|  | // Convenience method for C++ code | 
|  | NPVariantProxy& operator=(const std::string& strval) { | 
|  | assignFrom(variant, strval); | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | // Convenience method for C++ code | 
|  | static void assignFrom(NPVariant& variant, const std::string& strval) { | 
|  | NPVariant newvar; | 
|  | STDSTRING_TO_NPVARIANT(strval, newvar); | 
|  | assignFrom(variant, newvar); | 
|  | } | 
|  |  | 
|  | // Convenience method for C++ code | 
|  | NPVariantProxy& operator=(const char* strval) { | 
|  | assignFrom(variant, strval); | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | // Convenience method for C++ code | 
|  | static void assignFrom(NPVariant& variant, const char* strval) { | 
|  | NPVariant newvar; | 
|  | STRINGZ_TO_NPVARIANT(strval, newvar); | 
|  | assignFrom(variant, newvar); | 
|  | } | 
|  |  | 
|  | NPVariantProxy& operator=(const Value& newval) { | 
|  | assignFrom(plugin, variant, newval); | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | static void assignFrom(ScriptableInstance& plugin, NPVariant& variant, const Value& newval) { | 
|  | NPVariant newvar; | 
|  | VOID_TO_NPVARIANT(newvar); | 
|  | if (newval.isBoolean()) { | 
|  | BOOLEAN_TO_NPVARIANT(newval.getBoolean(), newvar); | 
|  | } else if (newval.isByte()) { | 
|  | INT32_TO_NPVARIANT(newval.getByte(), newvar); | 
|  | } else if (newval.isChar()) { | 
|  | INT32_TO_NPVARIANT(newval.getChar(), newvar); | 
|  | } else if (newval.isShort()) { | 
|  | INT32_TO_NPVARIANT(newval.getShort(), newvar); | 
|  | } else if (newval.isInt()) { | 
|  | int value = newval.getInt(); | 
|  | // Firefox NPAPI bug: 32-bit ints get mapped to int jsvals, regardless of range. | 
|  | // However, int jsvals are 31 bits, so we need to use a double if the value is | 
|  | // not representable in a 31 bit signed 2's-complement value. | 
|  | if (value >= 0x40000000 || value < -0x40000000) { | 
|  | DOUBLE_TO_NPVARIANT(static_cast<double>(value), newvar); | 
|  | } else { | 
|  | INT32_TO_NPVARIANT(value, newvar); | 
|  | } | 
|  | } else if (newval.isFloat()) { | 
|  | DOUBLE_TO_NPVARIANT(newval.getFloat(), newvar); | 
|  | } else if (newval.isDouble()) { | 
|  | DOUBLE_TO_NPVARIANT(newval.getDouble(), newvar); | 
|  | } else if (newval.isNull()) { | 
|  | NULL_TO_NPVARIANT(newvar); | 
|  | } else if (newval.isUndefined()) { | 
|  | VOID_TO_NPVARIANT(newvar); | 
|  | } else if (newval.isString()) { | 
|  | assignFrom(variant, newval.getString()); | 
|  | return; | 
|  | } else if (newval.isJavaObject()) { | 
|  | if (1) { | 
|  | JavaObject* jObj = plugin.createJavaWrapper(newval.getJavaObjectId()); | 
|  | NPObject* obj = jObj; | 
|  | OBJECT_TO_NPVARIANT(obj, newvar); | 
|  | } else { | 
|  | VOID_TO_NPVARIANT(newvar); | 
|  | } | 
|  | } else if (newval.isJsObject()) { | 
|  | OBJECT_TO_NPVARIANT(plugin.getLocalObject(newval.getJsObjectId()), | 
|  | newvar); | 
|  | } else { | 
|  | Debug::log(Debug::Error) << "Unsupported NPVariant type " << newval.getType() << Debug::flush; | 
|  | } | 
|  | assignFrom(variant, newvar); | 
|  | } | 
|  |  | 
|  | std::string toString() const { | 
|  | return toString(variant); | 
|  | } | 
|  |  | 
|  | static std::string toString(const NPVariant& variant) { | 
|  | std::string retval; | 
|  | // TODO(jat): remove sprintfs | 
|  | char buf[40]; | 
|  | NPObject* npObj; | 
|  | switch (variant.type) { | 
|  | case NPVariantType_Void: | 
|  | retval = "undef"; | 
|  | break; | 
|  | case NPVariantType_Null: | 
|  | retval = "null"; | 
|  | break; | 
|  | case NPVariantType_Bool: | 
|  | retval = "bool("; | 
|  | retval += (NPVARIANT_TO_BOOLEAN(variant) ? "true" : "false"); | 
|  | retval += ')'; | 
|  | break; | 
|  | case NPVariantType_Int32: | 
|  | retval = "int("; | 
|  | snprintf(buf, sizeof(buf), "%d)", NPVARIANT_TO_INT32(variant)); | 
|  | retval += buf; | 
|  | break; | 
|  | case NPVariantType_Double: | 
|  | retval = "double("; | 
|  | snprintf(buf, sizeof(buf), "%g)", NPVARIANT_TO_DOUBLE(variant)); | 
|  | retval += buf; | 
|  | break; | 
|  | case NPVariantType_String: | 
|  | { | 
|  | retval = "string("; | 
|  | NPString str = NPVARIANT_TO_STRING(variant); | 
|  | retval += std::string(str.UTF8Characters, str.UTF8Length); | 
|  | retval += ')'; | 
|  | } | 
|  | break; | 
|  | case NPVariantType_Object: | 
|  | npObj = NPVARIANT_TO_OBJECT(variant); | 
|  | if (JavaObject::isInstance(npObj)) { | 
|  | JavaObject* javaObj = static_cast<JavaObject*>(npObj); | 
|  | snprintf(buf, sizeof(buf), "javaObj(id=%d, ", javaObj->getObjectId()); | 
|  | } else { | 
|  | snprintf(buf, sizeof(buf), "jsObj(class=%p, ", npObj->_class); | 
|  | } | 
|  | retval = buf; | 
|  | snprintf(buf, sizeof(buf), "%p)", npObj); | 
|  | retval += buf; | 
|  | break; | 
|  | default: | 
|  | snprintf(buf, sizeof(buf), "Unknown type %d", variant.type); | 
|  | retval = buf; | 
|  | break; | 
|  | } | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | public: | 
|  | void release() { | 
|  | release(variant); | 
|  | } | 
|  |  | 
|  | static void release(NPVariant& variant) { | 
|  | NPN_ReleaseVariantValue(&variant); | 
|  | } | 
|  |  | 
|  | void retain() { | 
|  | retain(variant); | 
|  | } | 
|  |  | 
|  | static void retain(NPVariant& variant) { | 
|  | if (NPVARIANT_IS_OBJECT(variant)) { | 
|  | NPN_RetainObject(NPVARIANT_TO_OBJECT(variant)); | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | inline Debug::DebugStream& operator<<(Debug::DebugStream& dbg, const NPVariant& var) { | 
|  | return dbg << NPVariantProxy::toString(var); | 
|  | } | 
|  |  | 
|  | inline Debug::DebugStream& operator<<(Debug::DebugStream& dbg, const NPVariantProxy& var) { | 
|  | return dbg << var.toString(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Variation of NPVariantProxy that provides its own variant and always frees it | 
|  | * when the wrapper goes away. | 
|  | */ | 
|  | class NPVariantWrapper  { | 
|  | private: | 
|  | ScriptableInstance& plugin; | 
|  | NPVariant variant; | 
|  | public: | 
|  | NPVariantWrapper(ScriptableInstance& plugin) : plugin(plugin) { | 
|  | VOID_TO_NPVARIANT(variant); | 
|  | } | 
|  |  | 
|  | ~NPVariantWrapper() { | 
|  | release(); | 
|  | } | 
|  |  | 
|  | operator NPVariant() const { | 
|  | return variant; | 
|  | } | 
|  |  | 
|  | const NPVariant* operator->() const { | 
|  | return &variant; | 
|  | } | 
|  |  | 
|  | const NPVariant* address() const { | 
|  | return &variant; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Get the address for use as a return value.  Since the value can be trashed, | 
|  | * we need to release any data we currently hold. | 
|  | */ | 
|  | NPVariant* addressForReturn() { | 
|  | NPVariantProxy::release(variant); | 
|  | VOID_TO_NPVARIANT(variant); | 
|  | NPVariantProxy::retain(variant); // does nothing, present for consistency | 
|  | return &variant; | 
|  | } | 
|  |  | 
|  | int isInt() const { | 
|  | return NPVariantProxy::isInt(variant); | 
|  | } | 
|  |  | 
|  | int isObject() const { | 
|  | return NPVariantProxy::isObject(variant); | 
|  | } | 
|  |  | 
|  | int isString() const { | 
|  | return NPVariantProxy::isString(variant); | 
|  | } | 
|  |  | 
|  | int getAsInt() const { | 
|  | return NPVariantProxy::getAsInt(variant); | 
|  | } | 
|  |  | 
|  | NPObject* getAsObject() const { | 
|  | return NPVariantProxy::getAsObject(variant); | 
|  | } | 
|  |  | 
|  | const NPString* getAsNPString() const { | 
|  | return NPVariantProxy::getAsNPString(variant); | 
|  | } | 
|  |  | 
|  | Value getAsValue(ScriptableInstance& scriptInstance, bool unwrapJava = true) const { | 
|  | return NPVariantProxy::getAsValue(variant, scriptInstance, unwrapJava); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * The incoming variant is not altered, and is not even required to have | 
|  | * its contents retained.  Any object will get an extra refcount on it | 
|  | * when copied to this variant, and strings will be copied. | 
|  | */ | 
|  | NPVariantWrapper& operator=(const NPVariant& newval) { | 
|  | NPVariantProxy::assignFrom(variant, newval); | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | NPVariantWrapper& operator=(const Value& newval) { | 
|  | NPVariantProxy::assignFrom(plugin, variant, newval); | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | NPVariantWrapper& operator=(NPObject* obj) { | 
|  | NPVariantProxy::assignFrom(variant, obj); | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | // Convenience method for C++ code | 
|  | NPVariantWrapper& operator=(const std::string& strval) { | 
|  | NPVariantProxy::assignFrom(variant, strval); | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | // Convenience method for C++ code | 
|  | NPVariantWrapper& operator=(const char* strval) { | 
|  | NPVariantProxy::assignFrom(variant, strval); | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | // Convenience method for C++ code | 
|  | NPVariantWrapper& operator=(int intval) { | 
|  | NPVariantProxy::assignFrom(variant, intval); | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | void release() { | 
|  | NPVariantProxy::release(variant); | 
|  | } | 
|  |  | 
|  | void retain() { | 
|  | NPVariantProxy::retain(variant); | 
|  | } | 
|  |  | 
|  | std::string toString() const { | 
|  | return NPVariantProxy::toString(variant); | 
|  | } | 
|  | }; | 
|  |  | 
|  | inline Debug::DebugStream& operator<<(Debug::DebugStream& dbg, const NPVariantWrapper& var) { | 
|  | dbg << var.toString(); | 
|  | return dbg; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Maintains an array of NPVariants and cleans them up when it is destroyed. | 
|  | */ | 
|  | class NPVariantArray { | 
|  | private: | 
|  | ScriptableInstance& plugin; | 
|  | int size; | 
|  | NPVariant* args; | 
|  |  | 
|  | public: | 
|  | NPVariantArray(ScriptableInstance& plugin, int size) : plugin(plugin), size(size) { | 
|  | args = new NPVariant[size]; | 
|  | for (int i = 0; i < size; ++i) { | 
|  | VOID_TO_NPVARIANT(args[i]); | 
|  | } | 
|  | } | 
|  |  | 
|  | ~NPVariantArray() { | 
|  | for (int i = 0; i < size; ++i) { | 
|  | NPN_ReleaseVariantValue(&args[i]); | 
|  | } | 
|  | delete [] args; | 
|  | } | 
|  |  | 
|  | const NPVariant* getArray() const { | 
|  | return args; | 
|  | } | 
|  |  | 
|  | int getSize() const { | 
|  | return size; | 
|  | } | 
|  |  | 
|  | const NPVariant& operator[](int idx) const { | 
|  | if (idx >= size) { | 
|  | printf("NPVariantArray[idx=%d] const: size=%d\n", idx, size); | 
|  | } | 
|  | return args[idx]; | 
|  | } | 
|  |  | 
|  | NPVariantProxy operator[](int idx) { | 
|  | if (idx >= size) { | 
|  | printf("NPVariantArray[idx=%d]: size=%d\n", idx, size); | 
|  | } | 
|  | return NPVariantProxy(plugin, args[idx], true); | 
|  | } | 
|  | }; | 
|  |  | 
|  | inline Debug::DebugStream& operator<<(Debug::DebugStream& dbg, const NPVariantArray& var) { | 
|  | dbg << "["; | 
|  | for (int i = 0; i < var.getSize(); ++i) { | 
|  | dbg << " " << var[i]; | 
|  | } | 
|  | dbg << " ]"; | 
|  | return dbg; | 
|  | } | 
|  |  | 
|  | #endif |