This commit is contained in:
David Rose 2009-07-08 07:49:54 +00:00
parent c415cf8dbf
commit 7127006958
8 changed files with 176 additions and 75 deletions

View File

@ -266,7 +266,9 @@ get_request() {
case P3D_RT_script:
handle_script_request(result);
break;
// Completely eat this request; don't return it to the caller.
finish_request(result, true);
return get_request();
default:
// Other kinds of requests don't require special handling at
@ -520,8 +522,14 @@ handle_script_request(P3D_request *request) {
case P3D_SO_set_property:
{
bool result = P3D_OBJECT_SET_PROPERTY(object, request->_request._script._property_name,
request->_request._script._value);
bool result;
if (request->_request._script._num_values == 1) {
result = P3D_OBJECT_SET_PROPERTY(object, request->_request._script._property_name,
request->_request._script._values[0]);
} else {
// Wrong number of values. Error.
result = false;
}
// Feed the result back down to the subprocess.
TiXmlDocument *doc = new TiXmlDocument;
@ -565,6 +573,36 @@ handle_script_request(P3D_request *request) {
_session->send_command(doc);
}
break;
case P3D_SO_call:
{
P3D_object *result =
P3D_OBJECT_CALL(object, request->_request._script._property_name,
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).
request->_request._script._num_values = 0;
// 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);
}
_session->send_command(doc);
}
break;
}
}

View File

@ -474,6 +474,7 @@ task_check_comm(GenericAsyncTask *task, void *user_data) {
////////////////////////////////////////////////////////////////////
TiXmlDocument *P3DPythonRun::
wait_script_response(int response_id) {
nout << "Waiting script_response " << response_id << "\n";
while (true) {
ACQUIRE_LOCK(_commands_lock);
@ -491,7 +492,7 @@ wait_script_response(int response_id) {
// This is the response we were waiting for.
_commands.erase(ci);
RELEASE_LOCK(_commands_lock);
nout << "received: " << *doc << "\n" << flush;
nout << "received script_response: " << *doc << "\n" << flush;
return doc;
}
}
@ -525,6 +526,7 @@ wait_script_response(int response_id) {
// process the input and append it to the queue.
Thread::force_yield();
}
assert(false);
}
////////////////////////////////////////////////////////////////////
@ -549,11 +551,11 @@ py_request_func(PyObject *args) {
// on the wire.
int response_id;
if (!PyArg_ParseTuple(extra_args, "i", &response_id)) {
Py_DECREF(extra_args);
return NULL;
}
TiXmlDocument *doc = wait_script_response(response_id);
assert(doc != NULL);
TiXmlElement *xcommand = doc->FirstChildElement("command");
assert(xcommand != NULL);
TiXmlElement *xvalue = xcommand->FirstChildElement("value");
@ -568,7 +570,6 @@ py_request_func(PyObject *args) {
}
delete doc;
Py_DECREF(extra_args);
return value;
}
@ -584,7 +585,6 @@ py_request_func(PyObject *args) {
// A general notification to be sent directly to the instance.
const char *message;
if (!PyArg_ParseTuple(extra_args, "s", &message)) {
Py_DECREF(extra_args);
return NULL;
}
@ -601,7 +601,6 @@ py_request_func(PyObject *args) {
int unique_id;
if (!PyArg_ParseTuple(extra_args, "sOsOi",
&operation, &object, &property_name, &value, &unique_id)) {
Py_DECREF(extra_args);
return NULL;
}
@ -611,8 +610,26 @@ py_request_func(PyObject *args) {
TiXmlElement *xobject = pyobj_to_xml(object);
xobject->SetValue("object");
xrequest->LinkEndChild(xobject);
TiXmlElement *xvalue = pyobj_to_xml(value);
xrequest->LinkEndChild(xvalue);
if (strcmp(operation, "call") == 0 && PySequence_Check(value)) {
// A special case: operation "call" receives a tuple of
// parameters; unpack the tuple for the XML.
Py_ssize_t length = PySequence_Length(value);
for (Py_ssize_t i = 0; i < length; ++i) {
PyObject *p = PySequence_GetItem(value, i);
if (p != NULL) {
TiXmlElement *xvalue = pyobj_to_xml(p);
xrequest->LinkEndChild(xvalue);
Py_DECREF(p);
}
}
} else {
// Other kinds of operations receive only a single parameter, if
// any.
TiXmlElement *xvalue = pyobj_to_xml(value);
xrequest->LinkEndChild(xvalue);
}
nout << "sent: " << doc << "\n" << flush;
_pipe_write << doc << flush;
@ -620,11 +637,9 @@ py_request_func(PyObject *args) {
} else {
string message = string("Unsupported request type: ") + string(request_type);
PyErr_SetString(PyExc_ValueError, message.c_str());
Py_DECREF(extra_args);
return NULL;
}
Py_DECREF(extra_args);
return Py_BuildValue("");
}

View File

@ -684,41 +684,54 @@ 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");
TiXmlElement *xvalue = xrequest->FirstChildElement("value");
int unique_id = 0;
xrequest->Attribute("unique_id", &unique_id);
if (operation != NULL && xobject != NULL) {
P3D_object *object = xml_to_p3dobj(xobject);
if (strcmp(operation, "get_property") == 0 && property_name != NULL) {
request = new P3D_request;
request->_request_type = P3D_RT_script;
request->_request._script._object = object;
request->_request._script._op = P3D_SO_get_property;
request->_request._script._property_name = strdup(property_name);
request->_request._script._value = NULL;
request->_request._script._unique_id = unique_id;
} else if (strcmp(operation, "set_property") == 0 && property_name != NULL && xvalue != NULL) {
request = new P3D_request;
request->_request_type = P3D_RT_script;
request->_request._script._object = object;
request->_request._script._op = P3D_SO_set_property;
request->_request._script._property_name = strdup(property_name);
request->_request._script._value = xml_to_p3dobj(xvalue);
request->_request._script._unique_id = unique_id;
} else if (strcmp(operation, "del_property") == 0 && property_name != NULL) {
request = new P3D_request;
request->_request_type = P3D_RT_script;
request->_request._script._object = object;
request->_request._script._op = P3D_SO_del_property;
request->_request._script._property_name = strdup(property_name);
request->_request._script._value = NULL;
request->_request._script._unique_id = unique_id;
P3D_script_operation op;
if (strcmp(operation, "get_property") == 0) {
op = P3D_SO_get_property;
} else if (strcmp(operation, "set_property") == 0) {
op = P3D_SO_set_property;
} else if (strcmp(operation, "del_property") == 0) {
op = P3D_SO_del_property;
} else if (strcmp(operation, "call") == 0) {
op = P3D_SO_call;
} else {
// An unexpected operation.
return NULL;
}
if (request == NULL) {
// If we haven't dispatched a request yet, we didn't use the
// object, so delete it.
P3D_OBJECT_FINISH(object);
request = new P3D_request;
request->_request_type = P3D_RT_script;
request->_request._script._op = op;
request->_request._script._object = xml_to_p3dobj(xobject);
request->_request._script._property_name = NULL;
if (property_name != NULL) {
request->_request._script._property_name = strdup(property_name);
}
request->_request._script._unique_id = unique_id;
// Fill in the value(s).
vector<P3D_object *> values;
TiXmlElement *xvalue = xrequest->FirstChildElement("value");
while (xvalue != NULL) {
P3D_object *value = xml_to_p3dobj(xvalue);
values.push_back(value);
xvalue = xvalue->NextSiblingElement("value");
}
if (values.empty()) {
// No values.
request->_request._script._values = NULL;
request->_request._script._num_values = 0;
} else {
// Some values.
int num_values = (int)values.size();
P3D_object **valuesp = new P3D_object *[num_values];
memcpy(valuesp, &values[0], num_values * sizeof(P3D_object *));
request->_request._script._values = valuesp;
request->_request._script._num_values = num_values;
}
}

View File

@ -609,7 +609,8 @@ typedef struct {
P3D_object *_object;
P3D_script_operation _op;
const char *_property_name;
P3D_object *_value;
P3D_object **_values;
int _num_values;
int _unique_id;
} P3D_request_script;

View File

@ -184,10 +184,39 @@ set_property(const string &property, P3D_object *value) {
////////////////////////////////////////////////////////////////////
P3D_object *PPBrowserObject::
call(const string &method_name, P3D_object *params[], int num_params) const {
// First, convert all of the parameters.
NPVariant *npparams = new NPVariant[num_params];
for (int i = 0; i < num_params; ++i) {
P3D_OBJECT_FINISH(params[i]);
_instance->p3dobj_to_variant(&npparams[i], params[i]);
}
return NULL;
NPVariant result;
if (method_name.empty()) {
// Call the default method.
if (!browser->invokeDefault(_instance->get_npp_instance(), _npobj,
npparams, num_params, &result)) {
// Failed to invoke.
logfile << "invoke failed\n" << flush;
delete[] npparams;
return NULL;
}
} else {
// Call the named method.
NPIdentifier method_id = browser->getstringidentifier(method_name.c_str());
if (!browser->invoke(_instance->get_npp_instance(), _npobj, method_id,
npparams, num_params, &result)) {
// Failed to invoke.
delete[] npparams;
return NULL;
}
}
logfile << "invoke succeeded\n" << flush;
P3D_object *object = _instance->variant_to_p3dobj(&result);
browser->releasevariantvalue(&result);
return object;
}
////////////////////////////////////////////////////////////////////

View File

@ -313,7 +313,7 @@ NPP_Print(NPP instance, NPPrint *platformPrint) {
////////////////////////////////////////////////////////////////////
int16
NPP_HandleEvent(NPP instance, void *event) {
logfile << "HandleEvent\n" << flush;
// logfile << "HandleEvent\n" << flush;
// Here's a fine opportunity to check for new requests.
PPInstance::handle_request_loop();

View File

@ -405,33 +405,6 @@ handle_request(P3D_request *request) {
cerr << "Got P3D_RT_notify: " << request->_request._notify._message
<< "\n";
// Ignore notifications.
/*
{
// Temporary.
P3D_object *obj = P3D_instance_get_panda_script_object(request->_instance);
cerr << "script_object is " << obj << "\n";
if (obj != NULL) {
if (P3D_OBJECT_SET_PROPERTY(obj, "foo", P3D_new_int_object(1))) {
cerr << "set_property succeeded\n";
P3D_object *prop = P3D_OBJECT_GET_PROPERTY(obj, "foo");
cerr << "get_property returned " << prop << "\n";
if (prop != NULL) {
int size = P3D_OBJECT_GET_STRING(prop, NULL, 0) + 1;
char *buffer = new char[size];
P3D_OBJECT_GET_STRING(prop, buffer, size);
cerr << " property is " << buffer << "\n";
delete buffer;
P3D_OBJECT_FINISH(prop);
}
} else {
cerr << "set_property failed\n";
}
P3D_OBJECT_FINISH(obj);
}
}
*/
break;
default:

View File

@ -284,7 +284,7 @@ class AppRunner(DirectObject):
def windowEvent(self, win):
self.sendRequest('notify', 'onwindowopen')
def scriptRequest(self, operation, object, propertyName = None,
def scriptRequest(self, operation, object, propertyName = '',
value = None):
""" Issues a new script request to the browser. This queries
or modifies one of the browser's DOM properties. This method
@ -364,12 +364,37 @@ class BrowserObject:
self.__dict__['_BrowserObject__runner'] = runner
self.__dict__['_BrowserObject__objectId'] = objectId
# This element is filled in by __getattr__; it connects
# the object to its parent.
self.__dict__['_BrowserObject__boundMethod'] = (None, None)
def __str__(self):
return "BrowserObject(%s)" % (self.__objectId)
parentObj, attribName = self.__boundMethod
if parentObj:
# Format it from its parent.
return "%s.%s" % (parentObj, attribName)
else:
# Format it directly.
return "BrowserObject(%s)" % (self.__objectId)
def __nonzero__(self):
return True
def __call__(self, *args):
parentObj, attribName = self.__boundMethod
if parentObj:
# Call it as a method.
result = self.__runner.scriptRequest('call', parentObj, propertyName = attribName, value = args)
else:
# Call it as a plain function.
result = self.__runner.scriptRequest('call', self, value = args)
if result is Null:
# Could not call the method.
raise TypeError
return result
def __getattr__(self, name):
""" Remaps attempts to query an attribute, as in obj.attr,
into the appropriate calls to query the actual browser object
@ -381,6 +406,13 @@ class BrowserObject:
# Failed to retrieve the attribute.
raise AttributeError(name)
if isinstance(value, BrowserObject):
# Fill in the parent object association, so __call__ can
# properly call a method. (Javascript needs to know the
# method container at the time of the call, and doesn't
# store it on the function object.)
value.__dict__['_BrowserObject__boundMethod'] = (self, name)
return value
def __setattr__(self, name, value):