ソースを参照

firefox fixes

David Rose 16 年 前
コミット
4dd7ebec82

+ 5 - 7
direct/src/plugin/p3dInstance.cxx

@@ -37,8 +37,6 @@ typedef P3DSplashWindow SplashWindowType;
 #endif
 #endif
 
-int P3DInstance::_next_instance_id = 0;
-
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DInstance::Constructor
 //       Access: Public
@@ -55,8 +53,8 @@ P3DInstance(P3D_request_ready_func *func, void *user_data) :
   _got_fparams = false;
   _got_wparams = false;
 
-  _instance_id = _next_instance_id;
-  ++_next_instance_id;
+  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
+  _instance_id = inst_mgr->get_unique_id();
 
   INIT_LOCK(_request_lock);
 
@@ -123,7 +121,7 @@ set_fparams(const P3DFileParams &fparams) {
   // For the moment, all sessions will be unique.
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   ostringstream strm;
-  strm << inst_mgr->get_unique_session_index();
+  strm << inst_mgr->get_unique_id();
   _session_key = strm.str();
 
   // TODO.
@@ -457,8 +455,8 @@ void P3DInstance::
 start_download(P3DDownload *download) {
   assert(download->get_download_id() == 0);
 
-  int download_id = _next_instance_id;
-  ++_next_instance_id;
+  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
+  int download_id = inst_mgr->get_unique_id();
   download->set_download_id(download_id);
 
   bool inserted = _downloads.insert(Downloads::value_type(download_id, download)).second;

+ 0 - 2
direct/src/plugin/p3dInstance.h

@@ -139,8 +139,6 @@ private:
   typedef deque<P3D_request *> BakedRequests;
   BakedRequests _baked_requests;
 
-  static int _next_instance_id;
-
   friend class P3DSession;
   friend class SplashDownload;
 };

+ 9 - 8
direct/src/plugin/p3dInstanceManager.cxx

@@ -40,7 +40,7 @@ P3DInstanceManager *P3DInstanceManager::_global_ptr;
 P3DInstanceManager::
 P3DInstanceManager() {
   _is_initialized = false;
-  _unique_session_index = 0;
+  _unique_id = 0;
 
   _notify_thread_continue = false;
   _started_notify_thread = false;
@@ -280,16 +280,17 @@ get_package(const string &package_name, const string &package_version,
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: P3DInstanceManager::get_unique_session_index
+//     Function: P3DInstanceManager::get_unique_id
 //       Access: Public
-//  Description: Returns a number used to uniquify the session_key for
-//               different instances.  This number is guaranteed to be
-//               different at each call.
+//  Description: Returns a number used to uniquify different
+//               instances.  This number is guaranteed to be different
+//               at each call, at least until the int space rolls
+//               over.
 ////////////////////////////////////////////////////////////////////
 int P3DInstanceManager::
-get_unique_session_index() {
-  ++_unique_session_index;
-  return _unique_session_index;
+get_unique_id() {
+  ++_unique_id;
+  return _unique_id;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 2 - 2
direct/src/plugin/p3dInstanceManager.h

@@ -61,7 +61,7 @@ public:
 
   inline int get_num_instances() const;
 
-  int get_unique_session_index();
+  int get_unique_id();
   void signal_request_ready(P3DInstance *inst);
 
   P3D_class_definition *make_class_definition() const;
@@ -100,7 +100,7 @@ private:
   typedef map<string, P3DPackage *> Packages;
   Packages _packages;
 
-  int _unique_session_index;
+  int _unique_id;
 
   // This condition var is waited on the main thread and signaled in a
   // sub-thread when new request notices arrive.

+ 16 - 1
direct/src/plugin/p3dPythonRun.cxx

@@ -32,6 +32,7 @@ P3DPythonRun(int argc, char *argv[]) {
   INIT_LOCK(_commands_lock);
   INIT_THREAD(_read_thread);
 
+  _session_id = 0;
   _next_sent_id = 0;
 
   _program_name = argv[0];
@@ -223,7 +224,20 @@ handle_command(TiXmlDocument *doc) {
 
     const char *cmd = xcommand->Attribute("cmd");
     if (cmd != NULL) {
-      if (strcmp(cmd, "start_instance") == 0) {
+      if (strcmp(cmd, "init") == 0) {
+        assert(!needs_response);
+
+        // The only purpose of the "init" command is to send us a
+        // unique session ID, which in fact we don't do much with.
+        xcommand->Attribute("session_id", &_session_id);
+
+        // We do use it to initiate our object id sequence with a
+        // number at least a little bit distinct from other sessions,
+        // though.  No technical requirement that we do this, but it
+        // does make debugging the logs a bit easier.
+        _next_sent_id = _session_id * 1000;
+
+      } else if (strcmp(cmd, "start_instance") == 0) {
         assert(!needs_response);
         TiXmlElement *xinstance = xcommand->FirstChildElement("instance");
         if (xinstance != (TiXmlElement *)NULL) {
@@ -405,6 +419,7 @@ handle_pyobj_command(TiXmlElement *xcommand, bool needs_response,
           if (PyArg_ParseTuple(params, "s", &property_name)) {
             if (PyObject_HasAttrString(obj, property_name)) {
               result = PyObject_GetAttrString(obj, property_name);
+
             } else {
               result = NULL;
             }

+ 2 - 0
direct/src/plugin/p3dPythonRun.h

@@ -105,6 +105,8 @@ private:
   typedef pmap<int, P3DCInstance *> Instances;
   Instances _instances;
 
+  int _session_id;
+
   string _program_name;
   int _py_argc;
   char **_py_argv;

+ 22 - 14
direct/src/plugin/p3dSession.cxx

@@ -38,13 +38,12 @@
 ////////////////////////////////////////////////////////////////////
 P3DSession::
 P3DSession(P3DInstance *inst) {
+  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
+  _session_id = inst_mgr->get_unique_id();
   _session_key = inst->get_session_key();
   _python_version = inst->get_python_version();
 
-  _next_sent_id = 0;
-
   _p3dpython_running = false;
-  _next_response_id = 0;
   _response = NULL;
   _got_response_id = -1;
 
@@ -58,8 +57,6 @@ P3DSession(P3DInstance *inst) {
   INIT_LOCK(_instances_lock);
   INIT_THREAD(_read_thread);
 
-  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-
   _panda3d = inst_mgr->get_package("panda3d", "dev", "Panda3D");
   _python_root_dir = _panda3d->get_package_dir();
   inst->add_package(_panda3d);
@@ -261,8 +258,8 @@ command_and_response(TiXmlDocument *command) {
     return NULL;
   }
 
-  int response_id = _next_response_id;
-  ++_next_response_id;
+  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
+  int response_id = inst_mgr->get_unique_id();
 
   // Add the "want_response_id" attribute to the toplevel command, so
   // the sub-process knows we'll be waiting for its response.
@@ -474,15 +471,14 @@ p3dobj_to_xml(P3D_object *obj) {
       // should pass a reference down to this particular object, so
       // the Python process knows to call back up to here to query it.
 
-      int object_id = _next_sent_id;
-      ++_next_sent_id;
+      P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
+      int object_id = inst_mgr->get_unique_id();
       bool inserted = _sent_objects.insert(SentObjects::value_type(object_id, obj)).second;
       while (!inserted) {
         // Hmm, we must have cycled around the entire int space?  Either
         // that, or there's a logic bug somewhere.  Assume the former,
         // and keep looking for an empty slot.
-        object_id = _next_sent_id;
-        ++_next_sent_id;
+        object_id = inst_mgr->get_unique_id();
         inserted = _sent_objects.insert(SentObjects::value_type(object_id, obj)).second;
       }
 
@@ -656,16 +652,28 @@ start_p3dpython() {
   
   spawn_read_thread();
 
-  // Now that the process has been started, feed it any commands we
-  // may have queued up.
+  // The very first command we send to the process is its session_id.
+  TiXmlDocument doc;
+  TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
+  TiXmlElement *xcommand = new TiXmlElement("command");
+  xcommand->SetAttribute("cmd", "init");
+  xcommand->SetAttribute("session_id", _session_id);
+  doc.LinkEndChild(decl);
+  doc.LinkEndChild(xcommand);
+  nout << "sent: " << doc << "\n" << flush;
+  _pipe_write << doc;
+  
+  // Also feed it any commands we may have queued up from before the
+  // process was started.
   Commands::iterator ci;
   for (ci = _commands.begin(); ci != _commands.end(); ++ci) {
     nout << "sent: " << *(*ci) << "\n" << flush;
     _pipe_write << *(*ci);
     delete (*ci);
   }
-  _pipe_write << flush;
   _commands.clear();
+
+  _pipe_write << flush;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 1 - 3
direct/src/plugin/p3dSession.h

@@ -98,6 +98,7 @@ private:
   };
 
 private:
+  int _session_id;
   string _session_key;
   string _python_version;
   string _output_filename;
@@ -119,7 +120,6 @@ private:
   // child process tells us it's safe to delete them.
   typedef map<int, P3D_object *> SentObjects;
   SentObjects _sent_objects;
-  int _next_sent_id;
 
   P3DPackage *_panda3d;
   PackageCallback *_panda3d_callback;
@@ -132,8 +132,6 @@ private:
 #endif
   bool _p3dpython_running;
 
-  int _next_response_id;
-
   // The _response_ready mutex protects this pointer.
   TiXmlDocument *_response;
   int _got_response_id;

+ 7 - 1
direct/src/plugin_npapi/ppPandaObject.cxx

@@ -221,7 +221,13 @@ has_property(NPIdentifier name) {
   // will never be called.  So we always say we *do* have any
   // particular property, whether we currently have it right now or
   // not (since we *could* have it if you call set_property()).
-  return true;
+
+  // On the other hand, Firefox gets confused about methods that are
+  // also properties.  So you have to say there's *no* property if
+  // there is in fact a callable method by that name, or Firefox will
+  // never call the method.
+  bool result = P3D_OBJECT_HAS_METHOD(_p3d_object, property_name.c_str());
+  return !result;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 64 - 1
direct/src/showutil/runp3d.py

@@ -518,7 +518,12 @@ class BrowserObject:
             value = self.__runner.scriptRequest('get_property', self,
                                                 propertyName = name)
         except EnvironmentError:
-            # Failed to retrieve the attribute.
+            # 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):
@@ -591,6 +596,64 @@ class BrowserObject:
             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()
     runner.gotWindow = True