blob: 6d7689d135e2096f4ebdf26bca0cb098e11e336b [file] [log] [blame]
#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 isBoolean() const {
return isBoolean(variant);
}
static int isBoolean(const NPVariant& variant) {
return NPVARIANT_IS_BOOLEAN(variant);
}
bool getAsBoolean() const {
return getAsBoolean(variant);
}
static bool getAsBoolean(const NPVariant& variant) {
return NPVARIANT_TO_BOOLEAN(variant);
}
int isInt() const {
return isInt(variant);
}
// Return true if the variant is holding a regular integer or an integral double.
static int isInt(const NPVariant& variant) {
if (NPVARIANT_IS_INT32(variant)) {
return 1;
} else if (NPVARIANT_IS_DOUBLE(variant)) {
// As of http://trac.webkit.org/changeset/72974 we get doubles for all
// numerical variants out of V8.
double d = NPVARIANT_TO_DOUBLE(variant);
int i = static_cast<int>(d);
// Verify that d is an integral value in range.
return (d == static_cast<double>(i));
} else {
return 0;
}
}
int getAsInt() const {
return getAsInt(variant);
}
static int getAsInt(const NPVariant& variant) {
if (isInt(variant)) {
if (NPVARIANT_IS_INT32(variant)) {
return NPVARIANT_TO_INT32(variant);
} else if (NPVARIANT_IS_DOUBLE(variant)) {
return static_cast<int>(NPVARIANT_TO_DOUBLE(variant));
}
}
Debug::log(Debug::Error) << "getAsInt: variant " <<
NPVariantProxy::toString(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;
}
bool isBoolean() const {
return NPVariantProxy::isBoolean(variant);
}
int isInt() const {
return NPVariantProxy::isInt(variant);
}
int isObject() const {
return NPVariantProxy::isObject(variant);
}
int isString() const {
return NPVariantProxy::isString(variant);
}
bool getAsBoolean() const {
return NPVariantProxy::getAsBoolean(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