Revision: 10438
Author:   cod...@google.com
Date:     Mon Jul 11 06:15:51 2011
Log: Adding DoNotRunWith annotation to failing JUnit test; possible HtmlUnit bug.
Review at http://gwt-code-reviews.appspot.com/1469803

Review by: con...@google.com
http://code.google.com/p/google-web-toolkit/source/detail?r=10438

Added:
 /trunk/plugins/npapi/NPVariantUtil.h
 /trunk/user/test/com/google/gwt/core/client/JsIdentityTest.java
Modified:
 /trunk/plugins/npapi/JavaObject.cpp
 /trunk/plugins/npapi/LocalObjectTable.h
 /trunk/plugins/npapi/NPVariantWrapper.h
 /trunk/plugins/npapi/ScriptableInstance.cpp
 /trunk/plugins/npapi/ScriptableInstance.h
 /trunk/plugins/npapi/prebuilt/gwt-dev-plugin/background.html
 /trunk/user/test/com/google/gwt/core/CoreSuite.java

=======================================
--- /dev/null
+++ /trunk/plugins/npapi/NPVariantUtil.h        Mon Jul 11 06:15:51 2011
@@ -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
=======================================
--- /dev/null
+++ /trunk/user/test/com/google/gwt/core/client/JsIdentityTest.java Mon Jul 11 06:15:51 2011
@@ -0,0 +1,199 @@
+/*
+ * 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.DoNotRunWith;
+import com.google.gwt.junit.Platform;
+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().
+   */
+  @DoNotRunWith(Platform.HtmlUnitUnknown)
+  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;
+  }-*/;
+}
=======================================
--- /trunk/plugins/npapi/JavaObject.cpp Thu Jul  7 10:09:23 2011
+++ /trunk/plugins/npapi/JavaObject.cpp Mon Jul 11 06:15:51 2011
@@ -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;
=======================================
--- /trunk/plugins/npapi/LocalObjectTable.h     Thu Jul  7 10:09:23 2011
+++ /trunk/plugins/npapi/LocalObjectTable.h     Mon Jul 11 06:15:51 2011
@@ -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
@@ -93,6 +150,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;
=======================================
--- /trunk/plugins/npapi/NPVariantWrapper.h     Thu Jul  7 10:09:23 2011
+++ /trunk/plugins/npapi/NPVariantWrapper.h     Mon Jul 11 06:15:51 2011
@@ -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 {
=======================================
--- /trunk/plugins/npapi/ScriptableInstance.cpp Thu Jul  7 10:09:23 2011
+++ /trunk/plugins/npapi/ScriptableInstance.cpp Mon Jul 11 06:15:51 2011
@@ -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 (NPVariantProxy::isObject(*result)) {
+  if (NPVariantUtil::isObject(*result)) {
+    dumpObjectBytes(NPVariantUtil::getAsObject(*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;
-  }
-  if (NPVariantProxy::isObject(*npValue)) {
-    dumpObjectBytes(NPVariantProxy::getAsObject(*npValue));
+ << NPVariantUtil::getAsObject(*npValue)->referenceCount << Debug::flush;
+  }
+  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.)
=======================================
--- /trunk/plugins/npapi/ScriptableInstance.h   Thu Jul  7 10:09:23 2011
+++ /trunk/plugins/npapi/ScriptableInstance.h   Mon Jul 11 06:15:51 2011
@@ -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);

=======================================
--- /trunk/plugins/npapi/prebuilt/gwt-dev-plugin/background.html Thu Jul 7 10:09:23 2011 +++ /trunk/plugins/npapi/prebuilt/gwt-dev-plugin/background.html Mon Jul 11 06:15:51 2011
@@ -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') {
=======================================
--- /trunk/user/test/com/google/gwt/core/CoreSuite.java Thu Jul 7 10:09:23 2011 +++ /trunk/user/test/com/google/gwt/core/CoreSuite.java Mon Jul 11 06:15:51 2011
@@ -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);

--
http://groups.google.com/group/Google-Web-Toolkit-Contributors

Reply via email to