Initial checkin of OOPHM plugins into trunk.  Testing of non-XPCOM plugins
is still required, and more platforms need to be built.


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@5868 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/plugins/npapi/NPVariantWrapper.h b/plugins/npapi/NPVariantWrapper.h
new file mode 100644
index 0000000..0a469c9
--- /dev/null
+++ b/plugins/npapi/NPVariantWrapper.h
@@ -0,0 +1,567 @@
+#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;
+  }
+
+  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