|
|
@@ -26,6 +26,10 @@ from pandac.PandaModules import VirtualFileSystem, Filename, Multifile, loadPrcF
|
|
|
from direct.stdpy import file
|
|
|
from direct.task.TaskManagerGlobal import taskMgr
|
|
|
from direct.showbase import AppRunnerGlobal
|
|
|
+
|
|
|
+# These imports are read by the C++ wrapper in p3dPythonRun.cxx.
|
|
|
+from direct.showutil.JavaScript import UndefinedObject, Undefined, ConcreteStruct, BrowserObject
|
|
|
+
|
|
|
import os
|
|
|
import types
|
|
|
import __builtin__
|
|
|
@@ -60,9 +64,8 @@ class AppRunner(DirectObject):
|
|
|
self.windowOpened = False
|
|
|
self.windowPrc = None
|
|
|
|
|
|
- # Store this Undefined instance where the application can easily
|
|
|
- # get to it.
|
|
|
self.Undefined = Undefined
|
|
|
+ self.ConcreteStruct = ConcreteStruct
|
|
|
|
|
|
# This is per session.
|
|
|
self.nextScriptId = 0
|
|
|
@@ -457,234 +460,6 @@ class AppRunner(DirectObject):
|
|
|
|
|
|
return (osFilename, tokens)
|
|
|
|
|
|
-class UndefinedObject:
|
|
|
- """ This is a special object that is returned by the browser to
|
|
|
- represent a NULL pointer, typically the return value for a failed
|
|
|
- operation. It has no attributes, similar to None. """
|
|
|
-
|
|
|
- def __nonzero__(self):
|
|
|
- return False
|
|
|
-
|
|
|
- def __str__(self):
|
|
|
- return "Undefined"
|
|
|
-
|
|
|
-# In fact, we normally always return this precise instance of the
|
|
|
-# UndefinedObject.
|
|
|
-Undefined = UndefinedObject()
|
|
|
-
|
|
|
-class BrowserObject:
|
|
|
- """ This class provides the Python wrapper around some object that
|
|
|
- actually exists in the plugin host's namespace, e.g. a JavaScript
|
|
|
- or DOM object. """
|
|
|
-
|
|
|
- def __init__(self, runner, objectId):
|
|
|
- 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 __del__(self):
|
|
|
- # When the BrowserObject destructs, tell the parent process it
|
|
|
- # doesn't need to keep around its corresponding P3D_object any
|
|
|
- # more.
|
|
|
- self.__runner.dropObject(self.__objectId)
|
|
|
-
|
|
|
- def __str__(self):
|
|
|
- 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):
|
|
|
- try:
|
|
|
- parentObj, attribName = self.__boundMethod
|
|
|
- if parentObj:
|
|
|
- # Call it as a method.
|
|
|
- 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.
|
|
|
- try:
|
|
|
- result = self.__runner.scriptRequest('call', parentObj, propertyName = attribName, value = args, needsResponse = needsResponse)
|
|
|
- except EnvironmentError:
|
|
|
- # Problem on the call. Maybe no such method?
|
|
|
- raise AttributeError
|
|
|
- else:
|
|
|
- # Call it as a plain function.
|
|
|
- result = self.__runner.scriptRequest('call', self, value = args)
|
|
|
- except EnvironmentError:
|
|
|
- # Some odd problem on the call.
|
|
|
- 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
|
|
|
- under the hood. """
|
|
|
-
|
|
|
- try:
|
|
|
- value = self.__runner.scriptRequest('get_property', self,
|
|
|
- propertyName = name)
|
|
|
- except EnvironmentError:
|
|
|
- # Failed to retrieve the attribute. But maybe there's a
|
|
|
- # method instead?
|
|
|
- if self.__runner.scriptRequest('has_method', self, propertyName = name):
|
|
|
- # Yes, so create a method wrapper for it.
|
|
|
- return MethodWrapper(self.__runner, self, name)
|
|
|
-
|
|
|
- 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):
|
|
|
- if name in self.__dict__:
|
|
|
- self.__dict__[name] = value
|
|
|
- return
|
|
|
-
|
|
|
- result = self.__runner.scriptRequest('set_property', self,
|
|
|
- propertyName = name,
|
|
|
- value = value)
|
|
|
- if not result:
|
|
|
- raise AttributeError(name)
|
|
|
-
|
|
|
- def __delattr__(self, name):
|
|
|
- if name in self.__dict__:
|
|
|
- del self.__dict__[name]
|
|
|
- return
|
|
|
-
|
|
|
- result = self.__runner.scriptRequest('del_property', self,
|
|
|
- propertyName = name)
|
|
|
- if not result:
|
|
|
- raise AttributeError(name)
|
|
|
-
|
|
|
- def __getitem__(self, key):
|
|
|
- """ Remaps attempts to query an attribute, as in obj['attr'],
|
|
|
- into the appropriate calls to query the actual browser object
|
|
|
- under the hood. Following the JavaScript convention, we treat
|
|
|
- obj['attr'] almost the same as obj.attr. """
|
|
|
-
|
|
|
- try:
|
|
|
- value = self.__runner.scriptRequest('get_property', self,
|
|
|
- propertyName = str(key))
|
|
|
- except EnvironmentError:
|
|
|
- # Failed to retrieve the property. We return IndexError
|
|
|
- # for numeric keys so we can properly support Python's
|
|
|
- # iterators, but we return KeyError for string keys to
|
|
|
- # emulate mapping objects.
|
|
|
- if isinstance(key, types.StringTypes):
|
|
|
- raise KeyError(key)
|
|
|
- else:
|
|
|
- raise IndexError(key)
|
|
|
-
|
|
|
- return value
|
|
|
-
|
|
|
- def __setitem__(self, key, value):
|
|
|
- result = self.__runner.scriptRequest('set_property', self,
|
|
|
- propertyName = str(key),
|
|
|
- value = value)
|
|
|
- if not result:
|
|
|
- if isinstance(key, types.StringTypes):
|
|
|
- raise KeyError(key)
|
|
|
- else:
|
|
|
- raise IndexError(key)
|
|
|
-
|
|
|
- def __delitem__(self, key):
|
|
|
- result = self.__runner.scriptRequest('del_property', self,
|
|
|
- propertyName = str(key))
|
|
|
- if not result:
|
|
|
- if isinstance(key, types.StringTypes):
|
|
|
- raise KeyError(key)
|
|
|
- else:
|
|
|
- raise IndexError(key)
|
|
|
-
|
|
|
-class MethodWrapper:
|
|
|
- """ This is a Python wrapper around a property of a BrowserObject
|
|
|
- that doesn't appear to be a first-class object in the Python
|
|
|
- sense, but is nonetheless a callable method. """
|
|
|
-
|
|
|
- def __init__(self, runner, parentObj, objectId):
|
|
|
- self.__dict__['_MethodWrapper__runner'] = runner
|
|
|
- self.__dict__['_MethodWraper__boundMethod'] = (parentObj, objectId)
|
|
|
-
|
|
|
- def __str__(self):
|
|
|
- parentObj, attribName = self.__boundMethod
|
|
|
- return "%s.%s" % (parentObj, attribName)
|
|
|
-
|
|
|
- def __nonzero__(self):
|
|
|
- return True
|
|
|
-
|
|
|
- def __call__(self, *args):
|
|
|
- try:
|
|
|
- parentObj, attribName = self.__boundMethod
|
|
|
- # Call it as a method.
|
|
|
- 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.
|
|
|
- try:
|
|
|
- result = self.__runner.scriptRequest('call', parentObj, propertyName = attribName, value = args, needsResponse = needsResponse)
|
|
|
- except EnvironmentError:
|
|
|
- # Problem on the call. Maybe no such method?
|
|
|
- raise AttributeError
|
|
|
-
|
|
|
- except EnvironmentError:
|
|
|
- # Some odd problem on the call.
|
|
|
- raise TypeError
|
|
|
-
|
|
|
- return result
|
|
|
-
|
|
|
- def __setattr__(self, name, value):
|
|
|
- """ setattr will generally fail on method objects. """
|
|
|
- raise AttributeError(name)
|
|
|
-
|
|
|
- def __delattr__(self, name):
|
|
|
- """ delattr will generally fail on method objects. """
|
|
|
- raise AttributeError(name)
|
|
|
-
|
|
|
if __name__ == '__main__':
|
|
|
runner = AppRunner(0)
|
|
|
runner.gotWindow = True
|