Updating npapi plugin to remove gwtId from Jso objects (idenity fix),
adds a second map in LocalObjects.
Review at http://gwt-code-reviews.appspot.com/1469803
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10423 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/plugins/npapi/JavaObject.cpp b/plugins/npapi/JavaObject.cpp
index 16f7a09..adbdbc8 100644
--- a/plugins/npapi/JavaObject.cpp
+++ b/plugins/npapi/JavaObject.cpp
@@ -18,6 +18,7 @@
#include "JavaObject.h"
#include "Plugin.h"
+#include "NPVariantUtil.h"
#include "NPVariantWrapper.h"
using std::string;
@@ -116,8 +117,8 @@
}
bool JavaObject::invokeDefault(const NPVariant *args, uint32_t argCount, NPVariant *result) {
- if (argCount < 2 || !NPVariantProxy::isInt(args[0])
- || (!NPVariantProxy::isNull(args[1]) && !NPVariantProxy::isObject(args[1]))) {
+ if (argCount < 2 || !NPVariantUtil::isInt(args[0])
+ || (!NPVariantUtil::isNull(args[1]) && !NPVariantUtil::isObject(args[1]))) {
Debug::log(Debug::Error) << "incorrect arguments to invokeDefault" << Debug::flush;
return false;
}
@@ -132,10 +133,10 @@
Debug::log(Debug::Debugging) << ", " << NPVariantProxy::toString(args[i]);
}
Debug::log(Debug::Debugging) << ")" << Debug::flush;
- int dispId = NPVariantProxy::getAsInt(args[0]);
+ int dispId = NPVariantUtil::getAsInt(args[0]);
int objId = objectId;
- if (!NPVariantProxy::isNull(args[1])) {
- NPObject* thisObj = NPVariantProxy::getAsObject(args[1]);
+ if (!NPVariantUtil::isNull(args[1])) {
+ NPObject* thisObj = NPVariantUtil::getAsObject(args[1]);
if (isInstance(thisObj)) {
JavaObject* thisJavaObj = static_cast<JavaObject*>(thisObj);
objId = thisJavaObj->objectId;
diff --git a/plugins/npapi/LocalObjectTable.h b/plugins/npapi/LocalObjectTable.h
index acfbf2f..57a5887 100644
--- a/plugins/npapi/LocalObjectTable.h
+++ b/plugins/npapi/LocalObjectTable.h
@@ -21,24 +21,43 @@
#include "Debug.h"
#include "mozincludes.h"
+#include "NPVariantUtil.h"
class LocalObjectTable {
private:
+ /* The host expects Value objects to have int's for JSO id's, hence the
+ * dual mapping. ObjectMap is for host communication (Value.getJsObjectId)
+ * and the IdMap is for browser communication (NPObject to ID).
+ */
typedef std::map<int, NPObject*> ObjectMap;
+ typedef std::map<NPObject*,int> IdMap;
+
+ NPP npp;
int nextId;
ObjectMap objects;
+ IdMap ids;
bool dontFree;
+ bool jsIdentitySafe;
+
+ const NPIdentifier gwtId;
+
void setFree(int id) {
- if (objects.erase(id) != 1) {
+ NPObject *obj = getById(id);
+ if(!obj) {
Debug::log(Debug::Error) << "setFree(id=" << id << "): object not in table"
<< Debug::flush;
+ return;
}
+ ids.erase(obj);
+ objects.erase(id);
}
public:
- LocalObjectTable(): nextId(0), dontFree(false) {
+ LocalObjectTable(NPP npp, bool jsIdentitySafe):
+ nextId(0), dontFree(false), jsIdentitySafe(jsIdentitySafe),
+ gwtId(NPN_GetStringIdentifier("__gwt_ObjectId")) {
}
virtual ~LocalObjectTable();
@@ -49,29 +68,67 @@
int add(NPObject* obj) {
int id = nextId++;
set(id, obj);
+
+ if (!jsIdentitySafe) {
+ NPVariant idVariant;
+ Debug::log(Debug::Debugging) << "LocalObjectTable::set(): setting expando("
+ << id << ")" << Debug::flush;
+ INT32_TO_NPVARIANT(id,idVariant);
+ if (!NPN_SetProperty(npp, obj, gwtId, &idVariant)) {
+ Debug::log(Debug::Error) << "Setting GWT id on object failed" << Debug::flush;
+ }
+ }
+
return id;
}
void set(int id, NPObject* obj) {
- Debug::log(Debug::Spam) << "LocalObjectTable::set(id=" << id << ",obj=" << (void*)obj
+ Debug::log(Debug::Debugging) << "LocalObjectTable::set(id=" << id << ",obj=" << (void*)obj
<< ")" << Debug::flush;
+ if (!jsIdentitySafe) {
+ ObjectMap::iterator it;
+ it = objects.find(id);
+ if( it != objects.end() ) {
+ if (it->second != obj) {
+ //The JSO has changed and we need to update the map, releasing
+ //the old and remembering the new object.
+ ids.erase(it->second);
+ NPN_ReleaseObject(it->second);
+ NPN_RetainObject(obj);
+ } else {
+ //do nothing; object exists and is already mapped
+ return;
+ }
+ } else {
+ //New insertion, retain the object in the table
+ NPN_RetainObject(obj);
+ }
+ } else {
+ //Not dealing with identity hack, retain
+ NPN_RetainObject(obj);
+ }
objects[id] = obj;
+ ids[obj] = id;
+
// keep track that we hold a reference in the table
- NPN_RetainObject(obj);
}
void free(int id) {
- Debug::log(Debug::Spam) << "LocalObjectTable::free(id=" << id << ")" << Debug::flush;
+ Debug::log(Debug::Debugging) << "LocalObjectTable::free(id=" << id << ")" << Debug::flush;
ObjectMap::iterator it = objects.find(id);
if (it == objects.end()) {
Debug::log(Debug::Error) << "Freeing freed object slot " << id << Debug::flush;
return;
}
+ if (!jsIdentitySafe) {
+ Debug::log(Debug::Debugging) << "removing expando!" << Debug::flush;
+ NPN_RemoveProperty(npp, it->second, gwtId);
+ }
+ setFree(id);
if (!dontFree) {
NPObject* obj = it->second;
NPN_ReleaseObject(obj);
}
- setFree(id);
}
void freeAll() {
@@ -85,7 +142,7 @@
objects.clear();
}
- NPObject* get(int id) {
+ NPObject* getById(int id) {
ObjectMap::iterator it = objects.find(id);
if (it == objects.end()) {
Debug::log(Debug::Error) << "LocalObjectTable::get(id=" << id
@@ -94,6 +151,30 @@
return it->second;
}
+ int getObjectId(NPObject* jsObject) {
+ int id = -1;
+ if(!jsIdentitySafe) {
+ NPVariant idVariant;
+ VOID_TO_NPVARIANT(idVariant);
+ Debug::log(Debug::Debugging) << "LocalObjectTable::get(): expando test"
+ << Debug::flush;
+ if (NPN_GetProperty(npp, jsObject, gwtId, &idVariant) &&
+ NPVariantUtil::isInt(idVariant)) {
+ id = NPVariantUtil::getAsInt(idVariant);
+ Debug::log(Debug::Debugging) << "LocalObjectTable::get(): expando: "
+ << id << Debug::flush;
+ set(id, jsObject);
+ }
+ NPN_ReleaseVariantValue(&idVariant);
+ } else {
+ IdMap::iterator it = ids.find(jsObject);
+ if (it != ids.end()) {
+ id = it->second;
+ }
+ }
+ return id;
+ }
+
void setDontFree(bool dontFree) {
this->dontFree = dontFree;
}
diff --git a/plugins/npapi/NPVariantUtil.h b/plugins/npapi/NPVariantUtil.h
new file mode 100644
index 0000000..2ca7ea5
--- /dev/null
+++ b/plugins/npapi/NPVariantUtil.h
@@ -0,0 +1,132 @@
+#ifndef _H_NPVariantUtil
+#define _H_NPVariantUtil
+
+#include "Debug.h"
+#include "mozincludes.h"
+
+/**
+ * Wraps an NPVariant and provides various helper functions
+ */
+class NPVariantUtil {
+public:
+ static int isBoolean(const NPVariant& variant) {
+ return NPVARIANT_IS_BOOLEAN(variant);
+ }
+
+ static bool getAsBoolean(const NPVariant& variant) {
+ return NPVARIANT_TO_BOOLEAN(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;
+ }
+ }
+
+ 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 " <<
+ NPVariantUtil::toString(variant) << "not int" << Debug::flush;
+ return 0;
+ }
+
+ static int isNull(const NPVariant& variant) {
+ return NPVARIANT_IS_NULL(variant);
+ }
+
+ static int isObject(const NPVariant& variant) {
+ return NPVARIANT_IS_OBJECT(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;
+ }
+
+ static int isString(const NPVariant& variant) {
+ return NPVARIANT_IS_STRING(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;
+ }
+
+ 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);
+ snprintf(buf, sizeof(buf), "obj(class=%p, ", npObj->_class);
+ retval = buf;
+ snprintf(buf, sizeof(buf), "count=%d, ", npObj->referenceCount);
+ 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;
+ }
+
+};
+
+#endif
diff --git a/plugins/npapi/NPVariantWrapper.h b/plugins/npapi/NPVariantWrapper.h
index 6d7689d..a1844c5 100644
--- a/plugins/npapi/NPVariantWrapper.h
+++ b/plugins/npapi/NPVariantWrapper.h
@@ -15,6 +15,7 @@
#include "Platform.h"
#include "mozincludes.h"
+#include "NPVariantUtil.h"
#include "Value.h"
#include "LocalObjectTable.h"
@@ -62,105 +63,39 @@
}
int isBoolean() const {
- return isBoolean(variant);
- }
-
- static int isBoolean(const NPVariant& variant) {
- return NPVARIANT_IS_BOOLEAN(variant);
+ return NPVariantUtil::isBoolean(variant);
}
bool getAsBoolean() const {
- return getAsBoolean(variant);
- }
-
- static bool getAsBoolean(const NPVariant& variant) {
- return NPVARIANT_TO_BOOLEAN(variant);
+ return NPVariantUtil::getAsBoolean(variant);
}
int isInt() const {
- return isInt(variant);
+ return NPVariantUtil::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;
+ return NPVariantUtil::getAsInt(variant);
}
int isNull() const {
- return isNull(variant);
+ return NPVariantUtil::isNull(variant);
}
- static int isNull(const NPVariant& variant) {
- return NPVARIANT_IS_NULL(variant);
- }
-
int isObject() const {
- return isObject(variant);
+ return NPVariantUtil::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;
+ return NPVariantUtil::getAsObject(variant);
}
int isString() const {
- return isString(variant);
+ return NPVariantUtil::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;
+ return NPVariantUtil::getAsNPString(variant);
}
Value getAsValue(ScriptableInstance& scriptInstance, bool unwrapJava = true) const {
@@ -465,35 +400,35 @@
}
bool isBoolean() const {
- return NPVariantProxy::isBoolean(variant);
+ return NPVariantUtil::isBoolean(variant);
}
int isInt() const {
- return NPVariantProxy::isInt(variant);
+ return NPVariantUtil::isInt(variant);
}
int isObject() const {
- return NPVariantProxy::isObject(variant);
+ return NPVariantUtil::isObject(variant);
}
int isString() const {
- return NPVariantProxy::isString(variant);
+ return NPVariantUtil::isString(variant);
}
bool getAsBoolean() const {
- return NPVariantProxy::getAsBoolean(variant);
+ return NPVariantUtil::getAsBoolean(variant);
}
int getAsInt() const {
- return NPVariantProxy::getAsInt(variant);
+ return NPVariantUtil::getAsInt(variant);
}
NPObject* getAsObject() const {
- return NPVariantProxy::getAsObject(variant);
+ return NPVariantUtil::getAsObject(variant);
}
const NPString* getAsNPString() const {
- return NPVariantProxy::getAsNPString(variant);
+ return NPVariantUtil::getAsNPString(variant);
}
Value getAsValue(ScriptableInstance& scriptInstance, bool unwrapJava = true) const {
diff --git a/plugins/npapi/ScriptableInstance.cpp b/plugins/npapi/ScriptableInstance.cpp
index 4344a5b..bc79d17 100644
--- a/plugins/npapi/ScriptableInstance.cpp
+++ b/plugins/npapi/ScriptableInstance.cpp
@@ -33,6 +33,8 @@
const static string INCLUDE_STR = "include";
const static string EXCLUDE_STR = "exclude";
+bool ScriptableInstance::jsIdentitySafe = false;
+
static inline string convertToString(const NPString& str) {
return string(GetNPStringUTF8Characters(str), GetNPStringUTF8Length(str));
}
@@ -58,7 +60,7 @@
ScriptableInstance::ScriptableInstance(NPP npp) : NPObjectWrapper<ScriptableInstance>(npp),
plugin(*reinterpret_cast<Plugin*>(npp->pdata)),
_channel(new HostChannel()),
- localObjects(),
+ localObjects(npp,ScriptableInstance::jsIdentitySafe),
_connectId(NPN_GetStringIdentifier("connect")),
initID(NPN_GetStringIdentifier("init")),
toStringID(NPN_GetStringIdentifier("toString")),
@@ -68,9 +70,9 @@
urlID(NPN_GetStringIdentifier("url")),
includeID(NPN_GetStringIdentifier("include")),
getHostPermissionID(NPN_GetStringIdentifier("getHostPermission")),
+ testJsIdentityID(NPN_GetStringIdentifier("testJsIdentity")),
connectedID(NPN_GetStringIdentifier("connected")),
statsID(NPN_GetStringIdentifier("stats")),
- gwtId(NPN_GetStringIdentifier("__gwt_ObjectId")),
jsDisconnectedID(NPN_GetStringIdentifier("__gwt_disconnected")),
jsInvokeID(NPN_GetStringIdentifier("__gwt_jsInvoke")),
jsResultID(NPN_GetStringIdentifier("__gwt_makeResult")),
@@ -82,6 +84,7 @@
if (NPN_GetValue(npp, NPNVWindowNPObject, &window) != NPERR_NO_ERROR) {
Debug::log(Debug::Error) << "Error getting window object" << Debug::flush;
}
+
}
ScriptableInstance::~ScriptableInstance() {
@@ -115,7 +118,7 @@
bool ScriptableInstance::tryGetStringPrimitive(NPObject* obj, NPVariant& result) {
if (NPN_HasMethod(getNPP(), obj, jsValueOfID)) {
if (NPN_Invoke(getNPP(), obj, jsValueOfID, 0, 0, &result)
- && NPVariantProxy::isString(result)) {
+ && NPVariantUtil::isString(result)) {
return true;
}
NPVariantProxy::release(result);
@@ -188,7 +191,8 @@
name == initID ||
name == toStringID ||
name == loadHostEntriesID ||
- name == getHostPermissionID) {
+ name == getHostPermissionID ||
+ name == testJsIdentityID ) {
return true;
}
return false;
@@ -214,6 +218,8 @@
loadHostEntries(args, argCount, result);
} else if (name == getHostPermissionID) {
getHostPermission(args, argCount, result);
+ } else if (name == testJsIdentityID) {
+ testJsIdentity(args, argCount, result);
}
return true;
}
@@ -242,7 +248,7 @@
//=============================================================================
void ScriptableInstance::init(const NPVariant* args, unsigned argCount, NPVariant* result) {
- if (argCount != 1 || !NPVariantProxy::isObject(args[0])) {
+ if (argCount != 1 || !NPVariantUtil::isObject(args[0])) {
// TODO: better failure?
Debug::log(Debug::Error) << "ScriptableInstance::init called with incorrect arguments:\n";
for (unsigned i = 0; i < argCount; ++i) {
@@ -256,7 +262,7 @@
NPN_ReleaseObject(window);
}
// replace our window object with that passed by the caller
- window = NPVariantProxy::getAsObject(args[0]);
+ window = NPVariantUtil::getAsObject(args[0]);
NPN_RetainObject(window);
BOOLEAN_TO_NPVARIANT(true, *result);
result->type = NPVariantType_Bool;
@@ -271,7 +277,7 @@
//window.location.href
NPN_GetProperty(getNPP(), locationVariant.getAsObject(), hrefID, hrefVariant.addressForReturn());
- const NPString* locationHref = NPVariantProxy::getAsNPString(hrefVariant);
+ const NPString* locationHref = NPVariantUtil::getAsNPString(hrefVariant);
return convertToString(*locationHref);
}
@@ -282,7 +288,7 @@
AllowedConnections::clearRules();
for (unsigned i = 0; i < argCount; ++i) {
//Get the host entry object {url: "somehost.net", include: true/false}
- NPObject* hostEntry = NPVariantProxy::getAsObject(args[i]);
+ NPObject* hostEntry = NPVariantUtil::getAsObject(args[i]);
if (!hostEntry) {
Debug::log(Debug::Error) << "Got a host entry that is not an object.\n";
break;
@@ -322,7 +328,7 @@
}
void ScriptableInstance::getHostPermission(const NPVariant* args, unsigned argCount, NPVariant* result) {
- if (argCount != 1 || !NPVariantProxy::isString(args[0])) {
+ if (argCount != 1 || !NPVariantUtil::isString(args[0])) {
Debug::log(Debug::Error) << "ScriptableInstance::getHostPermission called with incorrect arguments.\n";
}
@@ -347,12 +353,34 @@
NPVariantProxy(*this, *result) = retStr;
}
+void ScriptableInstance::testJsIdentity(const NPVariant* args, unsigned argCount, NPVariant* result) {
+ if (argCount != 2 || !NPVariantUtil::isObject(args[0]) ||
+ !NPVariantUtil::isObject(args[1]) ) {
+ Debug::log(Debug::Error) << "ScriptableInstance::testJsIdentity called"
+ << " with incorrect arguments.\n";
+ }
+ NPObject* obj1 = NPVariantUtil::getAsObject(args[0]);
+ NPObject* obj2 = NPVariantUtil::getAsObject(args[1]);
+ Debug::log(Debug::Info) << "obj1:" << obj1 << " obj2:" << obj2
+ << Debug::flush;
+ if( obj1 == obj2 ) {
+ Debug::log(Debug::Info) << "Idenity check passed; not using expando!"
+ << Debug::flush;
+ ScriptableInstance::jsIdentitySafe = true;
+ } else {
+ Debug::log(Debug::Info) << "Idenity check failed; using expando"
+ << Debug::flush;
+ ScriptableInstance::jsIdentitySafe = false;
+ }
+}
+
+
void ScriptableInstance::connect(const NPVariant* args, unsigned argCount, NPVariant* result) {
- if (argCount != 5 || !NPVariantProxy::isString(args[0])
- || !NPVariantProxy::isString(args[1])
- || !NPVariantProxy::isString(args[2])
- || !NPVariantProxy::isString(args[3])
- || !NPVariantProxy::isString(args[4])) {
+ if (argCount != 5 || !NPVariantUtil::isString(args[0])
+ || !NPVariantUtil::isString(args[1])
+ || !NPVariantUtil::isString(args[2])
+ || !NPVariantUtil::isString(args[3])
+ || !NPVariantUtil::isString(args[4])) {
// TODO: better failure?
Debug::log(Debug::Error) << "ScriptableInstance::connect called with incorrect arguments:\n";
for (unsigned i = 0; i < argCount; ++i) {
@@ -432,18 +460,9 @@
}
int ScriptableInstance::getLocalObjectRef(NPObject* obj) {
- NPVariantWrapper wrappedRetVal(*this);
- int id;
- if (NPN_GetProperty(getNPP(), obj, gwtId, wrappedRetVal.addressForReturn())
- && wrappedRetVal.isInt()) {
- id = wrappedRetVal.getAsInt();
- localObjects.set(id, obj);
- } else {
+ int id = localObjects.getObjectId(obj);
+ if(id == -1) {
id = localObjects.add(obj);
- wrappedRetVal = id;
- if (!NPN_SetProperty(getNPP(), obj, gwtId, wrappedRetVal.address())) {
- Debug::log(Debug::Error) << "Setting GWT id on object failed" << Debug::flush;
- }
}
return id;
}
@@ -464,13 +483,8 @@
void ScriptableInstance::freeValue(HostChannel& channel, int idCount, const int* const ids) {
Debug::log(Debug::Debugging) << "freeValue(#ids=" << idCount << ")" << Debug::flush;
for (int i = 0; i < idCount; ++i) {
- Debug::log(Debug::Spam) << " id=" << ids[i] << Debug::flush;
- NPObject* obj = localObjects.get(ids[i]);
- if (!NPN_RemoveProperty(getNPP(), obj, gwtId)) {
- Debug::log(Debug::Error) << "Unable to remove GWT ID from object " << ids[i] << Debug::flush;
- } else {
- localObjects.free(ids[i]);
- }
+ Debug::log(Debug::Spam) << " id=" << ids[i] << Debug::flush;
+ localObjects.free(ids[i]);
}
}
@@ -494,7 +508,7 @@
return Value();
}
int id = args[0].getInt();
- NPObject* obj = localObjects.get(id);
+ NPObject* obj = localObjects.getById(id);
NPIdentifier propID;
if (args[1].isString()) {
string propName = args[1].getString();
@@ -523,7 +537,7 @@
return Value();
}
int id = args[0].getInt();
- NPObject* obj = localObjects.get(id);
+ NPObject* obj = localObjects.getById(id);
NPIdentifier propID;
if (args[1].isString()) {
string propName = args[1].getString();
@@ -677,7 +691,7 @@
Value propertyValue = ServerMethods::getProperty(*_channel, this, objectId, dispId);
if (propertyValue.isJsObject()) {
// TODO(jat): special-case for testing
- NPObject* npObj = localObjects.get(propertyValue.getJsObjectId());
+ NPObject* npObj = localObjects.getById(propertyValue.getJsObjectId());
OBJECT_TO_NPVARIANT(npObj, *result);
NPN_RetainObject(npObj);
} else {
@@ -685,12 +699,12 @@
}
Debug::log(Debug::Debugging) << " return val=" << propertyValue
<< ", NPVariant=" << *result << Debug::flush;
- if (NPVariantProxy::isObject(*result)) {
- dumpObjectBytes(NPVariantProxy::getAsObject(*result));
+ if (NPVariantUtil::isObject(*result)) {
+ dumpObjectBytes(NPVariantUtil::getAsObject(*result));
}
- if (NPVariantProxy::isObject(*result)) {
+ if (NPVariantUtil::isObject(*result)) {
Debug::log(Debug::Debugging) << " final return refcount = "
- << NPVariantProxy::getAsObject(*result)->referenceCount << Debug::flush;
+ << NPVariantUtil::getAsObject(*result)->referenceCount << Debug::flush;
}
return true;
}
@@ -699,17 +713,17 @@
const NPVariant* npValue) {
Debug::log(Debug::Debugging) << "JavaObject_setProperty(objectid="
<< objectId << ", dispId=" << dispId << ", value=" << *npValue << ")" << Debug::flush;
- if (NPVariantProxy::isObject(*npValue)) {
+ if (NPVariantUtil::isObject(*npValue)) {
Debug::log(Debug::Debugging) << " before localObj: refcount = "
- << NPVariantProxy::getAsObject(*npValue)->referenceCount << Debug::flush;
+ << NPVariantUtil::getAsObject(*npValue)->referenceCount << Debug::flush;
}
Value value = NPVariantProxy::getAsValue(*npValue, *this, true);
- if (NPVariantProxy::isObject(*npValue)) {
+ if (NPVariantUtil::isObject(*npValue)) {
Debug::log(Debug::Debugging) << " after localObj: refcount = "
- << NPVariantProxy::getAsObject(*npValue)->referenceCount << Debug::flush;
+ << NPVariantUtil::getAsObject(*npValue)->referenceCount << Debug::flush;
}
- if (NPVariantProxy::isObject(*npValue)) {
- dumpObjectBytes(NPVariantProxy::getAsObject(*npValue));
+ if (NPVariantUtil::isObject(*npValue)) {
+ dumpObjectBytes(NPVariantUtil::getAsObject(*npValue));
}
Debug::log(Debug::Debugging) << " as value: " << value << Debug::flush;
// TODO: no way to set an actual exception object! (Could ClassCastException on server.)
diff --git a/plugins/npapi/ScriptableInstance.h b/plugins/npapi/ScriptableInstance.h
index a24e085..1bea653 100644
--- a/plugins/npapi/ScriptableInstance.h
+++ b/plugins/npapi/ScriptableInstance.h
@@ -64,7 +64,8 @@
void dumpJSresult(const char* js);
int getLocalObjectRef(NPObject* obj);
- NPObject* getLocalObject(int refid) { return localObjects.get(refid); }
+
+ NPObject* getLocalObject(int refid) { return localObjects.getById(refid); }
bool tryGetStringPrimitive(NPObject* obj, NPVariant& result);
@@ -87,6 +88,7 @@
HostChannel* _channel;
LocalObjectTable localObjects;
+ static bool jsIdentitySafe;
int savedValueIdx;
// Identifiers
@@ -100,10 +102,10 @@
const NPIdentifier urlID;
const NPIdentifier includeID;
const NPIdentifier getHostPermissionID;
+ const NPIdentifier testJsIdentityID;
const NPIdentifier connectedID;
const NPIdentifier statsID;
- const NPIdentifier gwtId;
const NPIdentifier jsDisconnectedID;
const NPIdentifier jsInvokeID;
@@ -133,7 +135,7 @@
void init(const NPVariant* args, unsigned argCount, NPVariant* result);
void loadHostEntries(const NPVariant* args, unsigned argCount, NPVariant* result);
void getHostPermission(const NPVariant* args, unsigned argCount, NPVariant* result);
-
+ void testJsIdentity(const NPVariant* args, unsigned argCount, NPVariant* result);
Value clientMethod_getProperty(HostChannel& channel, int numArgs, const Value* const args);
Value clientMethod_setProperty(HostChannel& channel, int numArgs, const Value* const args);
diff --git a/plugins/npapi/prebuilt/gwt-dev-plugin/background.html b/plugins/npapi/prebuilt/gwt-dev-plugin/background.html
index fe60956..73f674d 100644
--- a/plugins/npapi/prebuilt/gwt-dev-plugin/background.html
+++ b/plugins/npapi/prebuilt/gwt-dev-plugin/background.html
@@ -65,6 +65,9 @@
var icon = null;
console.log("got permission " + permission + " for host " + host + '/ code ' + code);
+ var idObject = {};
+ plugin.testJsIdentity( idObject, idObject );
+
if (permission == 'include') {
icon = enabledIcon;
} else if (permission == 'exclude') {
diff --git a/user/test/com/google/gwt/core/CoreSuite.java b/user/test/com/google/gwt/core/CoreSuite.java
index 0e7b5d6..4be1c9e 100644
--- a/user/test/com/google/gwt/core/CoreSuite.java
+++ b/user/test/com/google/gwt/core/CoreSuite.java
@@ -20,6 +20,7 @@
import com.google.gwt.core.client.JavaScriptExceptionTest;
import com.google.gwt.core.client.JsArrayMixedTest;
import com.google.gwt.core.client.JsArrayTest;
+import com.google.gwt.core.client.JsIdentityTest;
import com.google.gwt.core.client.SchedulerTest;
import com.google.gwt.core.client.ScriptInjectorTest;
import com.google.gwt.core.client.impl.AsyncFragmentLoaderTest;
@@ -43,6 +44,7 @@
suite.addTestSuite(GWTTest.class);
suite.addTestSuite(HttpThrowableReporterTest.class);
suite.addTestSuite(JavaScriptExceptionTest.class);
+ suite.addTestSuite(JsIdentityTest.class);
suite.addTestSuite(JsArrayTest.class);
suite.addTestSuite(JsArrayMixedTest.class);
suite.addTestSuite(SchedulerImplTest.class);
diff --git a/user/test/com/google/gwt/core/client/JsIdentityTest.java b/user/test/com/google/gwt/core/client/JsIdentityTest.java
new file mode 100644
index 0000000..b45cb01
--- /dev/null
+++ b/user/test/com/google/gwt/core/client/JsIdentityTest.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.client;
+
+import com.google.gwt.junit.client.GWTTestCase;
+
+/**
+ * Test Js Identity.
+ */
+public class JsIdentityTest extends GWTTestCase {
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.google.gwt.junit.client.GWTTestCase#getModuleName()
+ */
+ @Override
+ public String getModuleName() {
+ return "com.google.gwt.core.Core";
+ }
+
+ /**
+ * Store some JavaObjects in a JsArrayOf, then ask Javascript to run find
+ * elements via indexOf().
+ */
+ public void testJsIdentity() {
+ JsArrayOf<Object> elements = JsArrayOf.create();
+ Object id1 = new Object();
+ Object id2 = new Object();
+ Object id3 = new Object();
+ Object id4 = new Object();
+
+ elements.push(id1);
+ elements.push(id2);
+ elements.push(id3);
+ // id4 not pushed for failure test.
+
+ assertEquals(true, elements.contains(id1)); // pass (0)
+ assertEquals(true, elements.contains(id2)); // pass (1)
+ assertEquals(true, elements.contains(id3)); // pass (2)
+ assertEquals(false, elements.contains(id4)); // pass (-1)
+ }
+
+ /**
+ * Test that the same Java object passed twice is in fact the same in JS.
+ */
+ public void testJavaToJs() {
+ Object Foo = new Object();
+ assertTrue(jsID(Foo,Foo));
+ }
+
+ /**
+ * Store a JavaObject in Javascript, then pass the object back to JS to test
+ * for identity.
+ */
+ public void testJavaObjectStorage() {
+ Object Foo = new Object();
+ set(Foo);
+ assertTrue(isSet(Foo));
+ assertTrue(isStrictlySet(Foo));
+ }
+
+ /**
+ * Store a JavaScriptObject in Javascript, then fetch the JSO twice to test
+ * for identity (multiple NPObject requests).
+ */
+ public void testJsoJavaComparison() {
+ storeJsoIdentity();
+ JavaScriptObject obj1 = getJsoIdentity();
+ JavaScriptObject obj2 = getJsoIdentity();
+ assertSame(obj1,obj2);
+ }
+
+ /**
+ * Store a JavaObject in JavascriptArray and store it in an array, then try to
+ * fetch it back. Specific test for old plugin problem.
+ */
+ public void testJavaArrayArray() {
+ Object id1 = new Object();
+ Object id2 = new Object();
+ JsArray<JsoTestArray<Object>> elements = JavaScriptObject.createArray().cast();
+
+ elements.push(JsoTestArray.create(id1));
+ elements.push(JsoTestArray.create(id2));
+
+ Object get1 = elements.get(0).getT();
+ Object get2 = elements.get(1).getT();
+
+ assertEquals(2, elements.length());
+ assertSame(id1,get1);
+ assertSame(id2,get2);
+ }
+
+ static final class JsoTestArray<T> extends JavaScriptObject {
+ public static native <T> JsoTestArray<T> create(T cmd) /*-{
+ return [cmd, false];
+ }-*/;
+
+ protected JsoTestArray() {
+ }
+
+ /**
+ * Has implicit cast.
+ */
+ public native T getT() /*-{
+ return this[0];
+ }-*/;
+
+ public native boolean isRepeating() /*-{
+ return this[1];
+ }-*/;
+ }
+
+ static native void set(Object obj) /*-{
+ $wnd.__idTest_JavaObj = obj;
+ }-*/;
+
+ static native boolean isSet(Object obj) /*-{
+ return $wnd.__idTest_JavaObj == obj;
+ }-*/;
+
+ static native boolean isStrictlySet(Object obj) /*-{
+ return $wnd.__idTest_JavaObj === obj;
+ }-*/;
+
+ static native void storeJsoIdentity() /*-{
+ var idObject = {
+ something : 'another',
+ another : 1234
+ };
+ $wnd.__idTest_Jso = idObject;
+ }-*/;
+
+ static native JavaScriptObject getJsoIdentity() /*-{
+ return $wnd.__idTest_Jso;
+ }-*/;
+
+ static native boolean jsID(Object a, Object b) /*-{
+ return a === b;
+ }-*/;
+}
+
+/**
+ * JavaScript native implementation for arrays.
+ */
+final class JsArrayOf<T> extends JavaScriptObject {
+
+ /**
+ * Create a new empty Array instance.
+ */
+ public static <T> JsArrayOf<T> create() {
+ return JavaScriptObject.createArray().cast();
+ }
+
+ protected JsArrayOf() {
+ }
+
+ public boolean contains(T value) {
+ return indexOf(value) != -1;
+ }
+
+ public native T get(int index) /*-{
+ return this[index];
+ }-*/;
+
+ public native int indexOf(T value) /*-{
+ return this.indexOf(value);
+ }-*/;
+
+ public native int length() /*-{
+ return this.length;
+ }-*/;
+
+ /**
+ * Pushes the given value onto the end of the array.
+ */
+ public native void push(T value) /*-{
+ this[this.length] = value;
+ }-*/;
+
+ public native void set(int index, T value) /*-{
+ this[index] = value;
+ }-*/;
+}