Browse Source

javascript eval

David Rose 16 years ago
parent
commit
aa171dde99

+ 1 - 1
direct/src/plugin/FileSpec.py

@@ -17,5 +17,5 @@ class FileSpec:
         self.hash = hv.asHex()
         self.hash = hv.asHex()
 
 
     def getParams(self):
     def getParams(self):
-        return 'filename="%s" size=%s timestamp=%s hash="%s"' % (
+        return 'filename="%s" size="%s" timestamp="%s" hash="%s"' % (
             self.filename, self.size, self.timestamp, self.hash)
             self.filename, self.size, self.timestamp, self.hash)

+ 66 - 8
direct/src/plugin/p3dInstance.cxx

@@ -475,7 +475,7 @@ handle_notify_request(P3D_request *request) {
   const char *message = request->_request._notify._message;
   const char *message = request->_request._notify._message;
   if (strcmp(message, "onwindowopen") == 0) {
   if (strcmp(message, "onwindowopen") == 0) {
     // The process told us that it just succesfully opened its
     // The process told us that it just succesfully opened its
-    // window.
+    // window.  Tear down the splash window.
     _instance_window_opened = true;
     _instance_window_opened = true;
     if (_splash_window != NULL) {
     if (_splash_window != NULL) {
       delete _splash_window;
       delete _splash_window;
@@ -495,6 +495,7 @@ handle_script_request(P3D_request *request) {
   assert(request->_request_type == P3D_RT_script);
   assert(request->_request_type == P3D_RT_script);
 
 
   P3D_object *object = request->_request._script._object;
   P3D_object *object = request->_request._script._object;
+  bool needs_response = request->_request._script._needs_response;
   int unique_id = request->_request._script._unique_id;
   int unique_id = request->_request._script._unique_id;
   switch (request->_request._script._op) {
   switch (request->_request._script._op) {
   case P3D_SO_get_property:
   case P3D_SO_get_property:
@@ -515,8 +516,12 @@ handle_script_request(P3D_request *request) {
         xcommand->LinkEndChild(_session->p3dobj_to_xml(result));
         xcommand->LinkEndChild(_session->p3dobj_to_xml(result));
         P3D_OBJECT_FINISH(result);
         P3D_OBJECT_FINISH(result);
       }
       }
-      
-      _session->send_command(doc);
+
+      if (needs_response) {
+        _session->send_command(doc);
+      } else {
+        delete doc;
+      }
     }
     }
     break;
     break;
 
 
@@ -546,7 +551,11 @@ handle_script_request(P3D_request *request) {
       xvalue->SetAttribute("value", (int)result);
       xvalue->SetAttribute("value", (int)result);
       xcommand->LinkEndChild(xvalue);
       xcommand->LinkEndChild(xvalue);
       
       
-      _session->send_command(doc);
+      if (needs_response) {
+        _session->send_command(doc);
+      } else {
+        delete doc;
+      }
     }
     }
     break;
     break;
 
 
@@ -570,7 +579,11 @@ handle_script_request(P3D_request *request) {
       xvalue->SetAttribute("value", (int)result);
       xvalue->SetAttribute("value", (int)result);
       xcommand->LinkEndChild(xvalue);
       xcommand->LinkEndChild(xvalue);
       
       
-      _session->send_command(doc);
+      if (needs_response) {
+        _session->send_command(doc);
+      } else {
+        delete doc;
+      }
     }
     }
     break;
     break;
 
 
@@ -581,8 +594,8 @@ handle_script_request(P3D_request *request) {
                         request->_request._script._values,
                         request->_request._script._values,
                         request->_request._script._num_values);
                         request->_request._script._num_values);
       // Reset the value count to 0 so we won't double-delete the
       // Reset the value count to 0 so we won't double-delete the
-      // values when the request is deleted (the above call has
-      // already deleted them).
+      // parameter values when the request is deleted (the above call
+      // has already deleted them).
       request->_request._script._num_values = 0;
       request->_request._script._num_values = 0;
 
 
       // Feed the result back down to the subprocess.
       // Feed the result back down to the subprocess.
@@ -600,7 +613,52 @@ handle_script_request(P3D_request *request) {
         P3D_OBJECT_FINISH(result);
         P3D_OBJECT_FINISH(result);
       }
       }
       
       
-      _session->send_command(doc);
+      if (needs_response) {
+        _session->send_command(doc);
+      } else {
+        delete doc;
+      }
+    }
+    break;
+
+  case P3D_SO_eval:
+    {
+      P3D_object *result;
+      if (request->_request._script._num_values == 1) {
+        P3D_object *expression = request->_request._script._values[0];
+        int size = P3D_OBJECT_GET_STRING(expression, NULL, 0);
+        char *buffer = new char[size + 1];
+        P3D_OBJECT_GET_STRING(expression, buffer, size + 1);
+        result = P3D_OBJECT_EVAL(object, buffer);
+        logfile << " eval " << *object << ": " << buffer << ", result = " << result << "\n";
+        delete[] buffer;
+      } else {
+        // Wrong number of values.  Error.
+        result = NULL;
+      }
+
+      // Feed the result back down to the subprocess.
+      TiXmlDocument *doc = new TiXmlDocument;
+      TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
+      TiXmlElement *xcommand = new TiXmlElement("command");
+      xcommand->SetAttribute("cmd", "script_response");
+      xcommand->SetAttribute("unique_id", unique_id);
+      
+      doc->LinkEndChild(decl);
+      doc->LinkEndChild(xcommand);
+
+      if (result != NULL) {
+        xcommand->LinkEndChild(_session->p3dobj_to_xml(result));
+        P3D_OBJECT_FINISH(result);
+      }
+
+      logfile << "eval  response: " << *doc << "\n";
+      
+      if (needs_response) {
+        _session->send_command(doc);
+      } else {
+        delete doc;
+      }
     }
     }
     break;
     break;
   }
   }

+ 23 - 0
direct/src/plugin/p3dObject.cxx

@@ -79,6 +79,11 @@ object_call(const P3D_object *object, const char *method_name,
   return ((const P3DObject *)object)->call(method_name, params, num_params);
   return ((const P3DObject *)object)->call(method_name, params, num_params);
 }
 }
 
 
+static P3D_object *
+object_eval(const P3D_object *object, const char *expression) {
+  return ((const P3DObject *)object)->eval(expression);
+}
+
 P3D_class_definition P3DObject::_object_class = {
 P3D_class_definition P3DObject::_object_class = {
   &object_finish,
   &object_finish,
   &object_copy,
   &object_copy,
@@ -91,6 +96,7 @@ P3D_class_definition P3DObject::_object_class = {
   &object_get_property,
   &object_get_property,
   &object_set_property,
   &object_set_property,
   &object_call,
   &object_call,
+  &object_eval,
 };
 };
 
 
 // The next functions are used to construct the generic
 // The next functions are used to construct the generic
@@ -166,6 +172,11 @@ generic_call(const P3D_object *object, const char *method_name,
   return NULL;
   return NULL;
 }
 }
 
 
+static P3D_object *
+generic_eval(const P3D_object *object, const char *expression) {
+  return NULL;
+}
+
 P3D_class_definition P3DObject::_generic_class = {
 P3D_class_definition P3DObject::_generic_class = {
   &generic_finish,
   &generic_finish,
   &generic_copy,
   &generic_copy,
@@ -178,6 +189,7 @@ P3D_class_definition P3DObject::_generic_class = {
   &generic_get_property,
   &generic_get_property,
   &generic_set_property,
   &generic_set_property,
   &generic_call,
   &generic_call,
+  &generic_eval,
 };
 };
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -301,6 +313,17 @@ call(const string &method_name, P3D_object *params[], int num_params) const {
   return NULL;
   return NULL;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: P3DObject::eval
+//       Access: Public, Virtual
+//  Description: Evaluates an arbitrary JavaScript expression.  None
+//               of the P3DObject classes implement this.
+////////////////////////////////////////////////////////////////////
+P3D_object *P3DObject::
+eval(const string &expression) const {
+  return NULL;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DObject::output
 //     Function: P3DObject::output
 //       Access: Public, Virtual
 //       Access: Public, Virtual

+ 1 - 0
direct/src/plugin/p3dObject.h

@@ -48,6 +48,7 @@ public:
 
 
   virtual P3D_object *call(const string &method_name, 
   virtual P3D_object *call(const string &method_name, 
                            P3D_object *params[], int num_params) const;
                            P3D_object *params[], int num_params) const;
+  virtual P3D_object *eval(const string &expression) const;
 
 
   virtual void output(ostream &out) const;
   virtual void output(ostream &out) const;
 
 

+ 5 - 2
direct/src/plugin/p3dPythonRun.cxx

@@ -598,14 +598,17 @@ py_request_func(PyObject *args) {
     PyObject *object;
     PyObject *object;
     const char *property_name;
     const char *property_name;
     PyObject *value;
     PyObject *value;
+    int needs_response;
     int unique_id;
     int unique_id;
-    if (!PyArg_ParseTuple(extra_args, "sOsOi", 
-                          &operation, &object, &property_name, &value, &unique_id)) {
+    if (!PyArg_ParseTuple(extra_args, "sOsOii", 
+                          &operation, &object, &property_name, &value, 
+                          &needs_response, &unique_id)) {
       return NULL;
       return NULL;
     }
     }
 
 
     xrequest->SetAttribute("operation", operation);
     xrequest->SetAttribute("operation", operation);
     xrequest->SetAttribute("property_name", property_name);
     xrequest->SetAttribute("property_name", property_name);
+    xrequest->SetAttribute("needs_response", (int)(needs_response != 0));
     xrequest->SetAttribute("unique_id", unique_id);
     xrequest->SetAttribute("unique_id", unique_id);
     TiXmlElement *xobject = pyobj_to_xml(object);
     TiXmlElement *xobject = pyobj_to_xml(object);
     xobject->SetValue("object");
     xobject->SetValue("object");

+ 5 - 0
direct/src/plugin/p3dSession.cxx

@@ -684,6 +684,8 @@ rt_make_p3d_request(TiXmlElement *xrequest) {
       const char *operation = xrequest->Attribute("operation");
       const char *operation = xrequest->Attribute("operation");
       TiXmlElement *xobject = xrequest->FirstChildElement("object");
       TiXmlElement *xobject = xrequest->FirstChildElement("object");
       const char *property_name = xrequest->Attribute("property_name");
       const char *property_name = xrequest->Attribute("property_name");
+      int needs_response = 0;
+      xrequest->Attribute("needs_response", &needs_response);
       int unique_id = 0;
       int unique_id = 0;
       xrequest->Attribute("unique_id", &unique_id);
       xrequest->Attribute("unique_id", &unique_id);
 
 
@@ -697,6 +699,8 @@ rt_make_p3d_request(TiXmlElement *xrequest) {
           op = P3D_SO_del_property;
           op = P3D_SO_del_property;
         } else if (strcmp(operation, "call") == 0) {
         } else if (strcmp(operation, "call") == 0) {
           op = P3D_SO_call;
           op = P3D_SO_call;
+        } else if (strcmp(operation, "eval") == 0) {
+          op = P3D_SO_eval;
         } else {
         } else {
           // An unexpected operation.
           // An unexpected operation.
           return NULL;
           return NULL;
@@ -710,6 +714,7 @@ rt_make_p3d_request(TiXmlElement *xrequest) {
         if (property_name != NULL) {
         if (property_name != NULL) {
           request->_request._script._property_name = strdup(property_name);
           request->_request._script._property_name = strdup(property_name);
         }
         }
+        request->_request._script._needs_response = (needs_response != 0);
         request->_request._script._unique_id = unique_id;
         request->_request._script._unique_id = unique_id;
         
         
         // Fill in the value(s).
         // Fill in the value(s).

+ 14 - 1
direct/src/plugin/p3d_plugin.h

@@ -74,7 +74,7 @@ extern "C" {
 libraries match.  It should be passed to P3D_initialize() (below).
 libraries match.  It should be passed to P3D_initialize() (below).
 This number will be incremented whenever there are changes to any of
 This number will be incremented whenever there are changes to any of
 the interface specifications defined in this header file. */
 the interface specifications defined in this header file. */
-#define P3D_API_VERSION 3
+#define P3D_API_VERSION 4
 
 
 /************************ GLOBAL FUNCTIONS **************************/
 /************************ GLOBAL FUNCTIONS **************************/
 
 
@@ -391,6 +391,15 @@ typedef P3D_object *
 P3D_object_call_method(const P3D_object *object, const char *method_name,
 P3D_object_call_method(const P3D_object *object, const char *method_name,
                        P3D_object *params[], int num_params);
                        P3D_object *params[], int num_params);
 
 
+/* Evaluates an arbitrary JavaScript expression in the context of the
+   object.
+
+   The return value is a newly-allocated P3D_object on success, or
+   NULL on failure.  Ownership of the return value is transferred to
+   the caller. */
+typedef P3D_object *
+P3D_object_eval_method(const P3D_object *object, const char *expression);
+
 /* This defines the class structure that implements all of the above
 /* This defines the class structure that implements all of the above
    methods. */
    methods. */
 typedef struct _P3D_class_definition {
 typedef struct _P3D_class_definition {
@@ -408,6 +417,7 @@ typedef struct _P3D_class_definition {
   P3D_object_set_property_method *_set_property;
   P3D_object_set_property_method *_set_property;
 
 
   P3D_object_call_method *_call;
   P3D_object_call_method *_call;
+  P3D_object_eval_method *_eval;
 
 
 } P3D_class_definition;
 } P3D_class_definition;
 
 
@@ -435,6 +445,7 @@ struct _P3D_object {
 #define P3D_OBJECT_SET_PROPERTY(object, property, value) ((object)->_class->_set_property((object), (property), (value)))
 #define P3D_OBJECT_SET_PROPERTY(object, property, value) ((object)->_class->_set_property((object), (property), (value)))
 
 
 #define P3D_OBJECT_CALL(object, method_name, params, num_params) ((object)->_class->_call((object), (method_name), (params), (num_params)))
 #define P3D_OBJECT_CALL(object, method_name, params, num_params) ((object)->_class->_call((object), (method_name), (params), (num_params)))
+#define P3D_OBJECT_EVAL(object, expression) ((object)->_class->_eval((object), (expression)))
 
 
 
 
 /* The following function types are once again meant to define
 /* The following function types are once again meant to define
@@ -604,6 +615,7 @@ typedef enum {
   P3D_SO_set_property,
   P3D_SO_set_property,
   P3D_SO_del_property,
   P3D_SO_del_property,
   P3D_SO_call,
   P3D_SO_call,
+  P3D_SO_eval,
 } P3D_script_operation;
 } P3D_script_operation;
 typedef struct {
 typedef struct {
   P3D_object *_object;
   P3D_object *_object;
@@ -611,6 +623,7 @@ typedef struct {
   const char *_property_name;
   const char *_property_name;
   P3D_object **_values;
   P3D_object **_values;
   int _num_values;
   int _num_values;
+  bool _needs_response;
   int _unique_id;
   int _unique_id;
 } P3D_request_script;
 } P3D_request_script;
 
 

+ 43 - 1
direct/src/plugin_npapi/ppBrowserObject.cxx

@@ -56,6 +56,11 @@ object_call(const P3D_object *object, const char *method_name,
   return ((const PPBrowserObject *)object)->call(method_name, params, num_params);
   return ((const PPBrowserObject *)object)->call(method_name, params, num_params);
 }
 }
 
 
+static P3D_object *
+object_eval(const P3D_object *object, const char *expression) {
+  return ((const PPBrowserObject *)object)->eval(expression);
+}
+
 P3D_class_definition *PPBrowserObject::_browser_object_class;
 P3D_class_definition *PPBrowserObject::_browser_object_class;
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -184,11 +189,15 @@ set_property(const string &property, P3D_object *value) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 P3D_object *PPBrowserObject::
 P3D_object *PPBrowserObject::
 call(const string &method_name, P3D_object *params[], int num_params) const {
 call(const string &method_name, P3D_object *params[], int num_params) const {
+  logfile << "call " << method_name << "(";
   // First, convert all of the parameters.
   // First, convert all of the parameters.
   NPVariant *npparams = new NPVariant[num_params];
   NPVariant *npparams = new NPVariant[num_params];
   for (int i = 0; i < num_params; ++i) {
   for (int i = 0; i < num_params; ++i) {
     _instance->p3dobj_to_variant(&npparams[i], params[i]);
     _instance->p3dobj_to_variant(&npparams[i], params[i]);
+    _instance->output_np_variant(logfile, npparams[i]);
+    logfile << ", ";
   }
   }
+  logfile << ")\n";
 
 
   NPVariant result;
   NPVariant result;
   if (method_name.empty()) {
   if (method_name.empty()) {
@@ -196,7 +205,7 @@ call(const string &method_name, P3D_object *params[], int num_params) const {
     if (!browser->invokeDefault(_instance->get_npp_instance(), _npobj,
     if (!browser->invokeDefault(_instance->get_npp_instance(), _npobj,
                                 npparams, num_params, &result)) {
                                 npparams, num_params, &result)) {
       // Failed to invoke.
       // Failed to invoke.
-      logfile << "invoke failed\n" << flush;
+      logfile << "invokeDefault failed\n" << flush;
       delete[] npparams;
       delete[] npparams;
       return NULL;
       return NULL;
     }
     }
@@ -207,11 +216,14 @@ call(const string &method_name, P3D_object *params[], int num_params) const {
     if (!browser->invoke(_instance->get_npp_instance(), _npobj, method_id,
     if (!browser->invoke(_instance->get_npp_instance(), _npobj, method_id,
                          npparams, num_params, &result)) {
                          npparams, num_params, &result)) {
       // Failed to invoke.
       // Failed to invoke.
+      logfile << "invoke failed\n" << flush;
       delete[] npparams;
       delete[] npparams;
       return NULL;
       return NULL;
     }
     }
   }
   }
 
 
+  delete[] npparams;
+
   logfile << "invoke succeeded\n" << flush;
   logfile << "invoke succeeded\n" << flush;
 
 
   P3D_object *object = _instance->variant_to_p3dobj(&result);
   P3D_object *object = _instance->variant_to_p3dobj(&result);
@@ -219,6 +231,35 @@ call(const string &method_name, P3D_object *params[], int num_params) const {
   return object;
   return object;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: PPBrowserObject::eval
+//       Access: Public
+//  Description: Evaluates the indicated JavaScript expression in the
+//               context of the object.
+////////////////////////////////////////////////////////////////////
+P3D_object *PPBrowserObject::
+eval(const string &expression) const {
+  logfile << "eval " << expression << "\n";
+
+  NPString npexpr;
+  npexpr.utf8characters = expression.c_str();
+  npexpr.utf8length = expression.length();
+
+  NPVariant result;
+  if (!browser->evaluate(_instance->get_npp_instance(), _npobj, 
+                         &npexpr, &result)) {
+    // Failed to eval.
+    logfile << "eval failed\n" << flush;
+    return NULL;
+  }
+
+  logfile << "eval succeeded\n" << flush;
+
+  P3D_object *object = _instance->variant_to_p3dobj(&result);
+  browser->releasevariantvalue(&result);
+  return object;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PPBrowserObject::get_class_definition
 //     Function: PPBrowserObject::get_class_definition
 //       Access: Private, Static
 //       Access: Private, Static
@@ -239,6 +280,7 @@ get_class_definition() {
     _browser_object_class->_get_property = &object_get_property;
     _browser_object_class->_get_property = &object_get_property;
     _browser_object_class->_set_property = &object_set_property;
     _browser_object_class->_set_property = &object_set_property;
     _browser_object_class->_call = &object_call;
     _browser_object_class->_call = &object_call;
+    _browser_object_class->_eval = &object_eval;
   }
   }
 
 
   return _browser_object_class;
   return _browser_object_class;

+ 1 - 0
direct/src/plugin_npapi/ppBrowserObject.h

@@ -41,6 +41,7 @@ public:
 
 
   P3D_object *call(const string &method_name, 
   P3D_object *call(const string &method_name, 
                    P3D_object *params[], int num_params) const;
                    P3D_object *params[], int num_params) const;
+  P3D_object *eval(const string &expression) const;
 
 
 private:
 private:
   static P3D_class_definition *get_class_definition();
   static P3D_class_definition *get_class_definition();

+ 13 - 21
direct/src/plugin_npapi/ppInstance.cxx

@@ -74,13 +74,9 @@ PPInstance::
     _p3d_inst = NULL;
     _p3d_inst = NULL;
   }
   }
 
 
-  // It's not clear why we shoudn't release this object now, but if we
-  // do we crash (at least on Windows).
-  /*
   if (_script_object != NULL) {
   if (_script_object != NULL) {
     browser->releaseobject(_script_object);
     browser->releaseobject(_script_object);
   }
   }
-  */
 
 
   // Free the tokens we allocated.
   // Free the tokens we allocated.
   Tokens::iterator ti;
   Tokens::iterator ti;
@@ -430,14 +426,11 @@ handle_request(P3D_request *request) {
         // with the proper P3D object pointer.
         // with the proper P3D object pointer.
         P3D_object *obj = P3D_instance_get_panda_script_object(_p3d_inst);
         P3D_object *obj = P3D_instance_get_panda_script_object(_p3d_inst);
         _script_object->set_p3d_object(obj);
         _script_object->set_p3d_object(obj);
+        logfile << "got onpythonload\n";
       }
       }
     }
     }
     break;
     break;
 
 
-  case P3D_RT_script:
-    // We're allowed to ignore this.
-    break;
-
   default:
   default:
     // Some request types are not handled.
     // Some request types are not handled.
     logfile << "Unhandled request: " << request->_request_type << "\n";
     logfile << "Unhandled request: " << request->_request_type << "\n";
@@ -481,6 +474,8 @@ handle_request_loop() {
 NPObject *PPInstance::
 NPObject *PPInstance::
 get_panda_script_object() {
 get_panda_script_object() {
   if (_script_object != NULL) {
   if (_script_object != NULL) {
+    // NPRuntime "steals" a reference to this object.
+    browser->retainobject(_script_object);
     return _script_object;
     return _script_object;
   }
   }
 
 
@@ -492,10 +487,7 @@ get_panda_script_object() {
 
 
   _script_object = PPPandaObject::make_new(this, obj);
   _script_object = PPPandaObject::make_new(this, obj);
 
 
-  // It's not clear why we need to explicitly retain this object now,
-  // but if we don't we crash.
   browser->retainobject(_script_object);
   browser->retainobject(_script_object);
-
   return _script_object;
   return _script_object;
 }
 }
 
 
@@ -946,28 +938,28 @@ send_window() {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PPInstance::show_np_variant
-//       Access: Private
+//     Function: PPInstance::output_np_variant
+//       Access: Public
 //  Description: Outputs the variant value.
 //  Description: Outputs the variant value.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PPInstance::
 void PPInstance::
-show_np_variant(const NPVariant &result) {
+output_np_variant(ostream &out, const NPVariant &result) {
   if (NPVARIANT_IS_NULL(result)) {
   if (NPVARIANT_IS_NULL(result)) {
-    logfile << "null";
+    out << "null";
   } else if (NPVARIANT_IS_VOID(result)) {
   } else if (NPVARIANT_IS_VOID(result)) {
-    logfile << "void";
+    out << "void";
   } else if (NPVARIANT_IS_BOOLEAN(result)) {
   } else if (NPVARIANT_IS_BOOLEAN(result)) {
-    logfile << "bool " << NPVARIANT_TO_BOOLEAN(result);
+    out << "bool " << NPVARIANT_TO_BOOLEAN(result);
   } else if (NPVARIANT_IS_INT32(result)) {
   } else if (NPVARIANT_IS_INT32(result)) {
-    logfile << "int " << NPVARIANT_TO_INT32(result);
+    out << "int " << NPVARIANT_TO_INT32(result);
   } else if (NPVARIANT_IS_DOUBLE(result)) {
   } else if (NPVARIANT_IS_DOUBLE(result)) {
-    logfile << "double " << NPVARIANT_TO_DOUBLE(result);
+    out << "double " << NPVARIANT_TO_DOUBLE(result);
   } else if (NPVARIANT_IS_STRING(result)) {
   } else if (NPVARIANT_IS_STRING(result)) {
     NPString str = NPVARIANT_TO_STRING(result);
     NPString str = NPVARIANT_TO_STRING(result);
-    logfile << "string " << string(str.utf8characters, str.utf8length);
+    out << "string " << string(str.utf8characters, str.utf8length);
   } else if (NPVARIANT_IS_OBJECT(result)) {
   } else if (NPVARIANT_IS_OBJECT(result)) {
     NPObject *child = NPVARIANT_TO_OBJECT(result);
     NPObject *child = NPVARIANT_TO_OBJECT(result);
-    logfile << "object " << child;
+    out << "object " << child;
   }
   }
 }
 }
 
 

+ 2 - 2
direct/src/plugin_npapi/ppInstance.h

@@ -58,6 +58,8 @@ public:
   void p3dobj_to_variant(NPVariant *result, const P3D_object *object);
   void p3dobj_to_variant(NPVariant *result, const P3D_object *object);
   P3D_object *variant_to_p3dobj(const NPVariant *variant);
   P3D_object *variant_to_p3dobj(const NPVariant *variant);
 
 
+  static void output_np_variant(ostream &out, const NPVariant &result);
+
 private:
 private:
   void start_download(const string &url, PPDownloadRequest *req);
   void start_download(const string &url, PPDownloadRequest *req);
   void downloaded_file(PPDownloadRequest *req, const string &filename);
   void downloaded_file(PPDownloadRequest *req, const string &filename);
@@ -72,8 +74,6 @@ private:
   void create_instance();
   void create_instance();
   void send_window();
   void send_window();
 
 
-  void show_np_variant(const NPVariant &result);
-
 #ifdef _WIN32
 #ifdef _WIN32
   static LONG 
   static LONG 
   window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
   window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);

+ 87 - 14
direct/src/showutil/runp3d.py

@@ -56,6 +56,7 @@ class AppRunner(DirectObject):
         self.gotWindow = False
         self.gotWindow = False
         self.gotP3DFilename = False
         self.gotP3DFilename = False
         self.started = False
         self.started = False
+        self.windowOpened = False
         self.windowPrc = None
         self.windowPrc = None
 
 
         # Store this Null instance where the application can easily
         # Store this Null instance where the application can easily
@@ -76,6 +77,10 @@ class AppRunner(DirectObject):
         # e.g. self.dom.document will be the document.
         # e.g. self.dom.document will be the document.
         self.dom = None
         self.dom = None
 
 
+        # This is the list of expressions we will evaluate when
+        # self.dom gets assigned.
+        self.deferredEvals = []
+
         # This is the default requestFunc that is installed if we
         # This is the default requestFunc that is installed if we
         # never call setRequestFunc().
         # never call setRequestFunc().
         def defaultRequestFunc(*args):
         def defaultRequestFunc(*args):
@@ -175,18 +180,27 @@ class AppRunner(DirectObject):
         self.dom = dom
         self.dom = dom
         print "setBrowserScriptObject(%s)" % (dom)
         print "setBrowserScriptObject(%s)" % (dom)
 
 
+        # Now evaluate any deferred expressions.
+        for expression in self.deferredEvals:
+            self.scriptRequest('eval', self.dom, value = expression,
+                               needsResponse = False)
+        self.deferredEvals = []
+
     def setP3DFilename(self, p3dFilename, tokens = [],
     def setP3DFilename(self, p3dFilename, tokens = [],
                        instanceId = None):
                        instanceId = None):
         # One day we will have support for multiple instances within a
         # One day we will have support for multiple instances within a
         # Python session.  Against that day, we save the instance ID
         # Python session.  Against that day, we save the instance ID
         # for this instance.
         # for this instance.
         self.instanceId = instanceId
         self.instanceId = instanceId
-        
-        # Now that we have an instanceId, we can respond to queries
-        # and such.
-        self.sendRequest('notify', 'onpythonload')
 
 
-        tokenDict = dict(tokens)
+        self.tokens = tokens
+        self.tokenDict = dict(tokens)
+
+        # Tell the browser that Python is up and running, and ready to
+        # respond to queries.
+        self.notifyRequest('onpythonload')
+
+        # Now go load the applet.
         fname = Filename.fromOsSpecific(p3dFilename)
         fname = Filename.fromOsSpecific(p3dFilename)
         if not p3dFilename:
         if not p3dFilename:
             # If we didn't get a literal filename, we have to download it
             # If we didn't get a literal filename, we have to download it
@@ -194,7 +208,7 @@ class AppRunner(DirectObject):
             fname = Filename.temporary('', 'p3d_')
             fname = Filename.temporary('', 'p3d_')
             fname.setExtension('p3d')
             fname.setExtension('p3d')
             p3dFilename = fname.toOsSpecific()
             p3dFilename = fname.toOsSpecific()
-            src = tokenDict.get('src', None)
+            src = self.tokenDict.get('src', None)
             if not src:
             if not src:
                 raise ArgumentError, "No Panda app specified."
                 raise ArgumentError, "No Panda app specified."
 
 
@@ -241,6 +255,7 @@ class AppRunner(DirectObject):
                 loadPrcFileData(pathname, data)
                 loadPrcFileData(pathname, data)
 
 
         self.gotP3DFilename = True
         self.gotP3DFilename = True
+
         self.startIfReady()
         self.startIfReady()
 
 
     def setupWindow(self, windowType, x, y, width, height, parent):
     def setupWindow(self, windowType, x, y, width, height, parent):
@@ -291,14 +306,53 @@ class AppRunner(DirectObject):
         self.requestFunc = func
         self.requestFunc = func
 
 
     def sendRequest(self, request, *args):
     def sendRequest(self, request, *args):
+        """ Delivers a request to the browser via self.requestFunc.
+        This low-level function is not intended to be called directly
+        by user code. """
+        
         assert self.requestFunc
         assert self.requestFunc
         return self.requestFunc(self.instanceId, request, args)
         return self.requestFunc(self.instanceId, request, args)
 
 
     def windowEvent(self, win):
     def windowEvent(self, win):
-        self.sendRequest('notify', 'onwindowopen')
-
+        """ This method is called when we get a window event.  We
+        listen for this to detect when the window has been
+        successfully opened. """
+
+        if not self.windowOpened:
+            self.notifyRequest('onwindowopen')
+            self.windowOpened = True
+
+    def notifyRequest(self, message):
+        """ Delivers a notify request to the browser.  This is a "this
+        happened" type notification; it optionally triggers some
+        JavaScript code execution, and may also trigger some internal
+        automatic actions.  (For instance, the plugin takes down the
+        splash window when it sees the onwindowopen notification. """
+
+        self.sendRequest('notify', message)
+
+        # Now process any JavaScript that might be waiting for the
+        # event as well.  These are the JavaScript expressions that
+        # were specified in the HTML embed or object tag.
+        #expression = self.tokenDict.get(message)
+        #if expression:
+        #    self.evalScript(expression)
+
+    def evalScript(self, expression):
+        """ Evaluates an arbitrary JavaScript expression in the global
+        DOM space.  This may be deferred if necessary if self.dom has
+        not yet been assigned. """
+
+        if not self.dom:
+            # Defer the expression.
+            self.deferredEvals.append(expression)
+        else:
+            # Evaluate it now.
+            self.scriptRequest('eval', self.dom, value = expression,
+                               needsResponse = False)
+        
     def scriptRequest(self, operation, object, propertyName = '',
     def scriptRequest(self, operation, object, propertyName = '',
-                      value = None):
+                      value = None, needsResponse = True):
         """ Issues a new script request to the browser.  This queries
         """ Issues a new script request to the browser.  This queries
         or modifies one of the browser's DOM properties.  This method
         or modifies one of the browser's DOM properties.  This method
         blocks until the return value is received from the browser,
         blocks until the return value is received from the browser,
@@ -320,11 +374,12 @@ class AppRunner(DirectObject):
         uniqueId = self.nextScriptId
         uniqueId = self.nextScriptId
         self.nextScriptId += 1
         self.nextScriptId += 1
         self.sendRequest('script', operation, object,
         self.sendRequest('script', operation, object,
-                         propertyName, value, uniqueId);
+                         propertyName, value, needsResponse, uniqueId);
 
 
-        # Now wait for the response to come in.
-        result = self.sendRequest('wait_script_response', uniqueId)
-        return result
+        if needsResponse:
+            # Now wait for the response to come in.
+            result = self.sendRequest('wait_script_response', uniqueId)
+            return result
 
 
     def parseSysArgs(self):
     def parseSysArgs(self):
         """ Converts sys.argv into (p3dFilename, tokens). """
         """ Converts sys.argv into (p3dFilename, tokens). """
@@ -397,7 +452,25 @@ class BrowserObject:
         parentObj, attribName = self.__boundMethod
         parentObj, attribName = self.__boundMethod
         if parentObj:
         if parentObj:
             # Call it as a method.
             # Call it as a method.
-            result = self.__runner.scriptRequest('call', parentObj, propertyName = attribName, value = args)
+            needsResponse = True
+            if parentObj is self.__runner.dom and attribName == 'alert':
+                # As a special hack, we don't wait for the return
+                # value from the alert() call, since this is a
+                # blocking call, and waiting for this could cause
+                # problems.
+                needsResponse = False
+
+            if parentObj is self.__runner.dom and attribName == 'eval' and len(args) == 1 and isinstance(args[0], types.StringTypes):
+                # As another special hack, we make dom.eval() a
+                # special case, and map it directly into an eval()
+                # call.  If the string begins with 'void ', we further
+                # assume we're not waiting for a response.
+                if args[0].startswith('void '):
+                    needsResponse = False
+                result = self.__runner.scriptRequest('eval', parentObj, value = args[0], needsResponse = needsResponse)
+            else:
+                # This is a normal method call.
+                result = self.__runner.scriptRequest('call', parentObj, propertyName = attribName, value = args, needsResponse = needsResponse)
         else:
         else:
             # Call it as a plain function.
             # Call it as a plain function.
             result = self.__runner.scriptRequest('call', self, value = args)
             result = self.__runner.scriptRequest('call', self, value = args)