From aa171dde997101a47bae1ce004e3e92cf9132947 Mon Sep 17 00:00:00 2001 From: David Rose Date: Wed, 8 Jul 2009 21:41:09 +0000 Subject: [PATCH] javascript eval --- direct/src/plugin/FileSpec.py | 2 +- direct/src/plugin/p3dInstance.cxx | 74 +++++++++++++-- direct/src/plugin/p3dObject.cxx | 23 +++++ direct/src/plugin/p3dObject.h | 1 + direct/src/plugin/p3dPythonRun.cxx | 7 +- direct/src/plugin/p3dSession.cxx | 5 ++ direct/src/plugin/p3d_plugin.h | 15 +++- direct/src/plugin_npapi/ppBrowserObject.cxx | 44 ++++++++- direct/src/plugin_npapi/ppBrowserObject.h | 1 + direct/src/plugin_npapi/ppInstance.cxx | 34 +++---- direct/src/plugin_npapi/ppInstance.h | 4 +- direct/src/showutil/runp3d.py | 99 ++++++++++++++++++--- 12 files changed, 260 insertions(+), 49 deletions(-) diff --git a/direct/src/plugin/FileSpec.py b/direct/src/plugin/FileSpec.py index 063280cf28..493d3a3652 100755 --- a/direct/src/plugin/FileSpec.py +++ b/direct/src/plugin/FileSpec.py @@ -17,5 +17,5 @@ class FileSpec: self.hash = hv.asHex() 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) diff --git a/direct/src/plugin/p3dInstance.cxx b/direct/src/plugin/p3dInstance.cxx index 46a8c898c0..61fe87d249 100644 --- a/direct/src/plugin/p3dInstance.cxx +++ b/direct/src/plugin/p3dInstance.cxx @@ -475,7 +475,7 @@ handle_notify_request(P3D_request *request) { const char *message = request->_request._notify._message; if (strcmp(message, "onwindowopen") == 0) { // The process told us that it just succesfully opened its - // window. + // window. Tear down the splash window. _instance_window_opened = true; if (_splash_window != NULL) { delete _splash_window; @@ -495,6 +495,7 @@ handle_script_request(P3D_request *request) { assert(request->_request_type == P3D_RT_script); P3D_object *object = request->_request._script._object; + bool needs_response = request->_request._script._needs_response; int unique_id = request->_request._script._unique_id; switch (request->_request._script._op) { case P3D_SO_get_property: @@ -515,8 +516,12 @@ handle_script_request(P3D_request *request) { xcommand->LinkEndChild(_session->p3dobj_to_xml(result)); P3D_OBJECT_FINISH(result); } - - _session->send_command(doc); + + if (needs_response) { + _session->send_command(doc); + } else { + delete doc; + } } break; @@ -546,7 +551,11 @@ handle_script_request(P3D_request *request) { xvalue->SetAttribute("value", (int)result); xcommand->LinkEndChild(xvalue); - _session->send_command(doc); + if (needs_response) { + _session->send_command(doc); + } else { + delete doc; + } } break; @@ -570,7 +579,11 @@ handle_script_request(P3D_request *request) { xvalue->SetAttribute("value", (int)result); xcommand->LinkEndChild(xvalue); - _session->send_command(doc); + if (needs_response) { + _session->send_command(doc); + } else { + delete doc; + } } break; @@ -581,8 +594,8 @@ handle_script_request(P3D_request *request) { request->_request._script._values, request->_request._script._num_values); // 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; // Feed the result back down to the subprocess. @@ -600,7 +613,52 @@ handle_script_request(P3D_request *request) { 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; } diff --git a/direct/src/plugin/p3dObject.cxx b/direct/src/plugin/p3dObject.cxx index 4e2bd849c3..e34738721a 100644 --- a/direct/src/plugin/p3dObject.cxx +++ b/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); } +static P3D_object * +object_eval(const P3D_object *object, const char *expression) { + return ((const P3DObject *)object)->eval(expression); +} + P3D_class_definition P3DObject::_object_class = { &object_finish, &object_copy, @@ -91,6 +96,7 @@ P3D_class_definition P3DObject::_object_class = { &object_get_property, &object_set_property, &object_call, + &object_eval, }; // 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; } +static P3D_object * +generic_eval(const P3D_object *object, const char *expression) { + return NULL; +} + P3D_class_definition P3DObject::_generic_class = { &generic_finish, &generic_copy, @@ -178,6 +189,7 @@ P3D_class_definition P3DObject::_generic_class = { &generic_get_property, &generic_set_property, &generic_call, + &generic_eval, }; //////////////////////////////////////////////////////////////////// @@ -301,6 +313,17 @@ call(const string &method_name, P3D_object *params[], int num_params) const { 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 // Access: Public, Virtual diff --git a/direct/src/plugin/p3dObject.h b/direct/src/plugin/p3dObject.h index efbecd9a23..13f0133a65 100644 --- a/direct/src/plugin/p3dObject.h +++ b/direct/src/plugin/p3dObject.h @@ -48,6 +48,7 @@ public: virtual P3D_object *call(const string &method_name, P3D_object *params[], int num_params) const; + virtual P3D_object *eval(const string &expression) const; virtual void output(ostream &out) const; diff --git a/direct/src/plugin/p3dPythonRun.cxx b/direct/src/plugin/p3dPythonRun.cxx index 0c74efd4aa..fe31cf7cc2 100755 --- a/direct/src/plugin/p3dPythonRun.cxx +++ b/direct/src/plugin/p3dPythonRun.cxx @@ -598,14 +598,17 @@ py_request_func(PyObject *args) { PyObject *object; const char *property_name; PyObject *value; + int needs_response; 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; } xrequest->SetAttribute("operation", operation); xrequest->SetAttribute("property_name", property_name); + xrequest->SetAttribute("needs_response", (int)(needs_response != 0)); xrequest->SetAttribute("unique_id", unique_id); TiXmlElement *xobject = pyobj_to_xml(object); xobject->SetValue("object"); diff --git a/direct/src/plugin/p3dSession.cxx b/direct/src/plugin/p3dSession.cxx index bbc0c35093..5dac9e0219 100644 --- a/direct/src/plugin/p3dSession.cxx +++ b/direct/src/plugin/p3dSession.cxx @@ -684,6 +684,8 @@ rt_make_p3d_request(TiXmlElement *xrequest) { const char *operation = xrequest->Attribute("operation"); TiXmlElement *xobject = xrequest->FirstChildElement("object"); const char *property_name = xrequest->Attribute("property_name"); + int needs_response = 0; + xrequest->Attribute("needs_response", &needs_response); int unique_id = 0; xrequest->Attribute("unique_id", &unique_id); @@ -697,6 +699,8 @@ rt_make_p3d_request(TiXmlElement *xrequest) { op = P3D_SO_del_property; } else if (strcmp(operation, "call") == 0) { op = P3D_SO_call; + } else if (strcmp(operation, "eval") == 0) { + op = P3D_SO_eval; } else { // An unexpected operation. return NULL; @@ -710,6 +714,7 @@ rt_make_p3d_request(TiXmlElement *xrequest) { if (property_name != NULL) { request->_request._script._property_name = strdup(property_name); } + request->_request._script._needs_response = (needs_response != 0); request->_request._script._unique_id = unique_id; // Fill in the value(s). diff --git a/direct/src/plugin/p3d_plugin.h b/direct/src/plugin/p3d_plugin.h index aeb9fa255c..a51e423cf0 100644 --- a/direct/src/plugin/p3d_plugin.h +++ b/direct/src/plugin/p3d_plugin.h @@ -74,7 +74,7 @@ extern "C" { libraries match. It should be passed to P3D_initialize() (below). This number will be incremented whenever there are changes to any of the interface specifications defined in this header file. */ -#define P3D_API_VERSION 3 +#define P3D_API_VERSION 4 /************************ GLOBAL FUNCTIONS **************************/ @@ -391,6 +391,15 @@ typedef P3D_object * P3D_object_call_method(const P3D_object *object, const char *method_name, 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 methods. */ typedef struct _P3D_class_definition { @@ -408,6 +417,7 @@ typedef struct _P3D_class_definition { P3D_object_set_property_method *_set_property; P3D_object_call_method *_call; + P3D_object_eval_method *_eval; } 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_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 @@ -604,6 +615,7 @@ typedef enum { P3D_SO_set_property, P3D_SO_del_property, P3D_SO_call, + P3D_SO_eval, } P3D_script_operation; typedef struct { P3D_object *_object; @@ -611,6 +623,7 @@ typedef struct { const char *_property_name; P3D_object **_values; int _num_values; + bool _needs_response; int _unique_id; } P3D_request_script; diff --git a/direct/src/plugin_npapi/ppBrowserObject.cxx b/direct/src/plugin_npapi/ppBrowserObject.cxx index e67df57331..6072a2f5d9 100644 --- a/direct/src/plugin_npapi/ppBrowserObject.cxx +++ b/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); } +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; //////////////////////////////////////////////////////////////////// @@ -184,11 +189,15 @@ set_property(const string &property, P3D_object *value) { //////////////////////////////////////////////////////////////////// P3D_object *PPBrowserObject:: call(const string &method_name, P3D_object *params[], int num_params) const { + logfile << "call " << method_name << "("; // First, convert all of the parameters. NPVariant *npparams = new NPVariant[num_params]; for (int i = 0; i < num_params; ++i) { _instance->p3dobj_to_variant(&npparams[i], params[i]); + _instance->output_np_variant(logfile, npparams[i]); + logfile << ", "; } + logfile << ")\n"; NPVariant result; 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, npparams, num_params, &result)) { // Failed to invoke. - logfile << "invoke failed\n" << flush; + logfile << "invokeDefault failed\n" << flush; delete[] npparams; 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, npparams, num_params, &result)) { // Failed to invoke. + logfile << "invoke failed\n" << flush; delete[] npparams; return NULL; } } + delete[] npparams; + logfile << "invoke succeeded\n" << flush; 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; } +//////////////////////////////////////////////////////////////////// +// 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 // Access: Private, Static @@ -239,6 +280,7 @@ get_class_definition() { _browser_object_class->_get_property = &object_get_property; _browser_object_class->_set_property = &object_set_property; _browser_object_class->_call = &object_call; + _browser_object_class->_eval = &object_eval; } return _browser_object_class; diff --git a/direct/src/plugin_npapi/ppBrowserObject.h b/direct/src/plugin_npapi/ppBrowserObject.h index d8a48436d7..46244d8b06 100644 --- a/direct/src/plugin_npapi/ppBrowserObject.h +++ b/direct/src/plugin_npapi/ppBrowserObject.h @@ -41,6 +41,7 @@ public: P3D_object *call(const string &method_name, P3D_object *params[], int num_params) const; + P3D_object *eval(const string &expression) const; private: static P3D_class_definition *get_class_definition(); diff --git a/direct/src/plugin_npapi/ppInstance.cxx b/direct/src/plugin_npapi/ppInstance.cxx index 6c81491418..022f6e76ba 100644 --- a/direct/src/plugin_npapi/ppInstance.cxx +++ b/direct/src/plugin_npapi/ppInstance.cxx @@ -74,13 +74,9 @@ PPInstance:: _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) { browser->releaseobject(_script_object); } - */ // Free the tokens we allocated. Tokens::iterator ti; @@ -430,14 +426,11 @@ handle_request(P3D_request *request) { // with the proper P3D object pointer. P3D_object *obj = P3D_instance_get_panda_script_object(_p3d_inst); _script_object->set_p3d_object(obj); + logfile << "got onpythonload\n"; } } break; - case P3D_RT_script: - // We're allowed to ignore this. - break; - default: // Some request types are not handled. logfile << "Unhandled request: " << request->_request_type << "\n"; @@ -481,6 +474,8 @@ handle_request_loop() { NPObject *PPInstance:: get_panda_script_object() { if (_script_object != NULL) { + // NPRuntime "steals" a reference to this object. + browser->retainobject(_script_object); return _script_object; } @@ -492,10 +487,7 @@ get_panda_script_object() { _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); - 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. //////////////////////////////////////////////////////////////////// void PPInstance:: -show_np_variant(const NPVariant &result) { +output_np_variant(ostream &out, const NPVariant &result) { if (NPVARIANT_IS_NULL(result)) { - logfile << "null"; + out << "null"; } else if (NPVARIANT_IS_VOID(result)) { - logfile << "void"; + out << "void"; } else if (NPVARIANT_IS_BOOLEAN(result)) { - logfile << "bool " << NPVARIANT_TO_BOOLEAN(result); + out << "bool " << NPVARIANT_TO_BOOLEAN(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)) { - logfile << "double " << NPVARIANT_TO_DOUBLE(result); + out << "double " << NPVARIANT_TO_DOUBLE(result); } else if (NPVARIANT_IS_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)) { NPObject *child = NPVARIANT_TO_OBJECT(result); - logfile << "object " << child; + out << "object " << child; } } diff --git a/direct/src/plugin_npapi/ppInstance.h b/direct/src/plugin_npapi/ppInstance.h index 56e356bdde..e33e13189f 100644 --- a/direct/src/plugin_npapi/ppInstance.h +++ b/direct/src/plugin_npapi/ppInstance.h @@ -58,6 +58,8 @@ public: void p3dobj_to_variant(NPVariant *result, const P3D_object *object); P3D_object *variant_to_p3dobj(const NPVariant *variant); + static void output_np_variant(ostream &out, const NPVariant &result); + private: void start_download(const string &url, PPDownloadRequest *req); void downloaded_file(PPDownloadRequest *req, const string &filename); @@ -72,8 +74,6 @@ private: void create_instance(); void send_window(); - void show_np_variant(const NPVariant &result); - #ifdef _WIN32 static LONG window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); diff --git a/direct/src/showutil/runp3d.py b/direct/src/showutil/runp3d.py index b86bd2d6ce..40463897ce 100755 --- a/direct/src/showutil/runp3d.py +++ b/direct/src/showutil/runp3d.py @@ -56,6 +56,7 @@ class AppRunner(DirectObject): self.gotWindow = False self.gotP3DFilename = False self.started = False + self.windowOpened = False self.windowPrc = None # 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. 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 # never call setRequestFunc(). def defaultRequestFunc(*args): @@ -175,18 +180,27 @@ class AppRunner(DirectObject): self.dom = 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 = [], instanceId = None): # One day we will have support for multiple instances within a # Python session. Against that day, we save the instance ID # for this instance. 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) if not p3dFilename: # 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.setExtension('p3d') p3dFilename = fname.toOsSpecific() - src = tokenDict.get('src', None) + src = self.tokenDict.get('src', None) if not src: raise ArgumentError, "No Panda app specified." @@ -241,6 +255,7 @@ class AppRunner(DirectObject): loadPrcFileData(pathname, data) self.gotP3DFilename = True + self.startIfReady() def setupWindow(self, windowType, x, y, width, height, parent): @@ -291,14 +306,53 @@ class AppRunner(DirectObject): self.requestFunc = func 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 return self.requestFunc(self.instanceId, request, args) 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 = '', - value = None): + value = None, needsResponse = True): """ Issues a new script request to the browser. This queries or modifies one of the browser's DOM properties. This method blocks until the return value is received from the browser, @@ -320,11 +374,12 @@ class AppRunner(DirectObject): uniqueId = self.nextScriptId self.nextScriptId += 1 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): """ Converts sys.argv into (p3dFilename, tokens). """ @@ -397,7 +452,25 @@ class BrowserObject: parentObj, attribName = self.__boundMethod if parentObj: # 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: # Call it as a plain function. result = self.__runner.scriptRequest('call', self, value = args)