瀏覽代碼

fix snow leopard issues some more. yeesh.

David Rose 16 年之前
父節點
當前提交
ecdbac1a2a
共有 2 個文件被更改,包括 238 次插入67 次删除
  1. 227 67
      direct/src/plugin_npapi/ppInstance.cxx
  2. 11 0
      direct/src/plugin_npapi/ppInstance.h

+ 227 - 67
direct/src/plugin_npapi/ppInstance.cxx

@@ -81,6 +81,12 @@ PPInstance(NPMIMEType pluginType, NPP instance, uint16_t mode,
   _root_dir = global_root_dir;
 
   _got_instance_url = false;
+  _opened_p3d_temp_file = false;
+  _finished_p3d_temp_file = false;
+  _p3d_temp_file_current_size = 0;
+  _p3d_temp_file_total_size = 0;
+  _p3d_instance_id = 0;
+
   _got_window = false;
   _python_window_open = false;
 
@@ -91,6 +97,7 @@ PPInstance(NPMIMEType pluginType, NPP instance, uint16_t mode,
   _run_loop_main = CFRunLoopGetCurrent();
   CFRetain(_run_loop_main);
   _request_timer = NULL;
+  INIT_LOCK(_timer_lock);
 #endif  // __APPLE__
 }
 
@@ -107,9 +114,11 @@ PPInstance::
   if (_request_timer != NULL) {
     CFRunLoopTimerInvalidate(_request_timer);
     CFRelease(_request_timer);
+    _request_timer = NULL;
   }
   _run_loop_main = CFRunLoopGetCurrent();
   CFRelease(_run_loop_main);
+  DESTROY_LOCK(_timer_lock);
 #endif  // __APPLE__
 
   if (_p3d_inst != NULL) {
@@ -124,6 +133,12 @@ PPInstance::
     browser->releaseobject(_script_object);
   }
 
+  if (!_p3d_temp_filename.empty()) {
+    _p3d_temp_file.close();
+    unlink(_p3d_temp_filename.c_str());
+    _p3d_temp_filename.clear();
+  }
+
   // Free the tokens we allocated.
   Tokens::iterator ti;
   for (ti = _tokens.begin(); ti != _tokens.end(); ++ti) {
@@ -254,6 +269,14 @@ new_stream(NPMIMEType type, NPStream *stream, bool seekable, uint16_t *stype) {
       _got_instance_url = true;
       _instance_url = stream->url;
       stream->notifyData = new PPDownloadRequest(PPDownloadRequest::RT_instance_data);
+
+      if (_p3d_inst != NULL) {
+        // If we already have an instance by the time we get this
+        // stream, start sending the data to the instance (instead of
+        // having to mess around with a temporary file).
+        _p3d_instance_id = P3D_instance_start_stream(_p3d_inst, _instance_url.c_str());
+        nout << "p3d instance to stream " << _p3d_instance_id << "\n";
+      }
       
       *stype = NP_NORMAL;
       _streams.push_back(stream);
@@ -334,29 +357,6 @@ stop_outstanding_streams() {
 ////////////////////////////////////////////////////////////////////
 int32_t PPInstance::
 write_ready(NPStream *stream) {
-  if (stream->notifyData != NULL && !_failed) {
-    PPDownloadRequest *req = (PPDownloadRequest *)(stream->notifyData);
-    if (req->_rtype == PPDownloadRequest::RT_instance_data) {
-      // There's a special case for the RT_instance_data stream.  This
-      // is the first, unsolicited stream that indicates the p3d
-      // instance data.  We have to send this stream into the
-      // instance, but we can only do this once the instance itself
-      // has been created.
-      if (_p3d_inst == NULL) {
-        // The instance hasn't yet been created.  We're not ready for
-        // data yet.
-        return 0;
-      }
-      // The instance has been created.  Redirect the stream into the
-      // instance.
-      assert(_got_instance_url);
-      int user_id = P3D_instance_start_stream(_p3d_inst, _instance_url.c_str());
-      nout << "Got p3d instance to stream " << user_id << "\n";
-      req->_rtype = PPDownloadRequest::RT_user;
-      req->_user_id = user_id;
-    }
-  }
-
   // We're supposed to return the maximum amount of data the plugin is
   // prepared to handle.  Gee, I don't know.  As much as you can give
   // me, I guess.
@@ -390,6 +390,46 @@ write_stream(NPStream *stream, int offset, int len, void *buffer) {
                                  P3D_RC_in_progress, 0,
                                  stream->end, buffer, len);
     return len;
+
+  case PPDownloadRequest::RT_instance_data:
+    // There's a special case for the RT_instance_data stream.  This
+    // is the first, unsolicited stream that indicates the p3d
+    // instance data.  We have to send this stream into the instance,
+    // but we can only do this once the instance itself has been
+    // created.
+
+    // We used to get away with returning 0 in write_ready until the
+    // instance was ready, but that turns out to fail under Safari
+    // Snow Leopard, which it seems will hold up every other download
+    // until the p3d file has been retrieved.  Sigh.  So we must start
+    // accepting the data even before the instance has been created,
+    // or we'll never get our contents.xml or any other important bits
+    // of data.
+
+    // Nowadays we solve this problem by writing the data to a
+    // temporary file until the instance is ready for it.
+    if (_p3d_inst == NULL) {
+      // The instance isn't ready, so stuff it in a temporary file.
+      if (!_opened_p3d_temp_file) {
+        open_p3d_temp_file();
+      }
+      _p3d_temp_file.write((const char *)buffer, len);
+      _p3d_temp_file_current_size += len;
+      _p3d_temp_file_total_size = stream->end;
+      return len;
+
+    } else {
+      // The instance has been created.  Redirect the stream into the
+      // instance.
+      assert(!_opened_p3d_temp_file);
+      req->_rtype = PPDownloadRequest::RT_user;
+      req->_user_id = _p3d_instance_id;
+      P3D_instance_feed_url_stream(_p3d_inst, req->_user_id,
+                                   P3D_RC_in_progress, 0,
+                                   stream->end, buffer, len);
+      return len;
+    }
+    break;
     
   default:
     nout << "Unexpected write_stream on " << stream->url << "\n";
@@ -422,17 +462,19 @@ destroy_stream(NPStream *stream, NPReason reason) {
   }
 
   PPDownloadRequest *req = (PPDownloadRequest *)(stream->notifyData);
+
+  P3D_result_code result_code = P3D_RC_done;
+  if (reason != NPRES_DONE) {
+    if (reason == NPRES_USER_BREAK) {
+      result_code = P3D_RC_shutdown;
+    } else {
+      result_code = P3D_RC_generic_error;
+    }
+  }
+
   switch (req->_rtype) {
   case PPDownloadRequest::RT_user:
     {
-      P3D_result_code result_code = P3D_RC_done;
-      if (reason != NPRES_DONE) {
-        if (reason == NPRES_USER_BREAK) {
-          result_code = P3D_RC_shutdown;
-        } else {
-          result_code = P3D_RC_generic_error;
-        }
-      }
       assert(!req->_notified_done);
       P3D_instance_feed_url_stream(_p3d_inst, req->_user_id,
                                    result_code, 0, stream->end, NULL, 0);
@@ -440,6 +482,26 @@ destroy_stream(NPStream *stream, NPReason reason) {
     }
     break;
 
+  case PPDownloadRequest::RT_instance_data:
+    if (_p3d_inst == NULL) {
+      // The instance still isn't ready; just mark the data done.
+      // We'll send the entire file to the instance when it is ready.
+      _finished_p3d_temp_file = true;
+      _p3d_temp_file_total_size = _p3d_temp_file_current_size;
+      if (result_code != P3D_RC_done) {
+        set_failed();
+      }
+
+    } else {
+      // The instance has (only just) been created.  Tell it we've
+      // sent it all the data it will get.
+      P3D_instance_feed_url_stream(_p3d_inst, _p3d_instance_id,
+                                   result_code, 0, stream->end, NULL, 0);
+    }
+    assert(!req->_notified_done);
+    req->_notified_done = true;
+    break;
+
   case PPDownloadRequest::RT_core_dll:
   case PPDownloadRequest::RT_contents_file:
     // These are received as a full-file only, so we don't care about
@@ -676,12 +738,14 @@ handle_request(P3D_request *request) {
 ////////////////////////////////////////////////////////////////////
 void PPInstance::
 generic_browser_call() {
+  /*
   if (!has_plugin_thread_async_call) {
     // If we can't ask Mozilla to call us back using
     // NPN_PluginThreadAsyncCall(), then we'll do it explicitly now,
     // since we know we're in the main thread here.
     handle_request_loop();
   }
+  */
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1025,20 +1089,17 @@ request_ready(P3D_instance *instance) {
 #ifdef __APPLE__
     // Use an OSX timer to forward this event to the main thread.
 
-    // Stop any previously-started timer--we don't need more than one.
-    if (inst->_request_timer != NULL) {
-      CFRunLoopTimerInvalidate(inst->_request_timer);
-      CFRelease(inst->_request_timer);
-      inst->_request_timer = NULL;
+    // Only set a new timer if we don't have one already started.
+    ACQUIRE_LOCK(inst->_timer_lock);
+    if (inst->_request_timer == NULL) {
+      CFRunLoopTimerContext timer_context;
+      memset(&timer_context, 0, sizeof(timer_context));
+      timer_context.info = inst;
+      inst->_request_timer = CFRunLoopTimerCreate
+        (NULL, 0, 0, 0, 0, timer_callback, &timer_context);
+      CFRunLoopAddTimer(inst->_run_loop_main, inst->_request_timer, kCFRunLoopCommonModes);
     }
-
-    // And start a new one.
-    CFRunLoopTimerContext timer_context;
-    memset(&timer_context, 0, sizeof(timer_context));
-    timer_context.info = inst;
-    inst->_request_timer = CFRunLoopTimerCreate
-      (NULL, 0, 0, 0, 0, timer_callback, &timer_context);
-    CFRunLoopAddTimer(inst->_run_loop_main, inst->_request_timer, kCFRunLoopCommonModes);
+    RELEASE_LOCK(inst->_timer_lock);
 #endif  // __APPLE__
 
     // Doesn't appear to be a reliable way to simulate this in Linux.
@@ -1197,6 +1258,83 @@ feed_file(PPDownloadRequest *req, const string &filename) {
   _file_datas.push_back(file_data);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PPInstance::open_p3d_temp_file
+//       Access: Private
+//  Description: Creates a temporary file into which the p3d file data
+//               is stored before the instance has been created.
+////////////////////////////////////////////////////////////////////
+void PPInstance::
+open_p3d_temp_file() {
+  assert(!_opened_p3d_temp_file);
+  _opened_p3d_temp_file = true;
+  _finished_p3d_temp_file = false;
+  _p3d_temp_file_current_size = 0;
+  _p3d_temp_file_total_size = 0;
+
+  char *name = tempnam(NULL, "p3d_");
+  _p3d_temp_filename = name;
+  free(name);
+
+  _p3d_temp_file.clear();
+  _p3d_temp_file.open(_p3d_temp_filename.c_str(), ios::binary);
+  if (!_p3d_temp_file) {
+    nout << "Unable to open temp file " << _p3d_temp_filename << "\n";
+    set_failed();
+  } else {
+    nout << "Opening " << _p3d_temp_filename
+         << " for storing preliminary p3d data\n";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPInstance::send_p3d_temp_file_data
+//       Access: Private
+//  Description: Once the instance has been created, sends it all of
+//               the data we have saved up for it while we were
+//               waiting.
+////////////////////////////////////////////////////////////////////
+void PPInstance::
+send_p3d_temp_file_data() {
+  assert(_opened_p3d_temp_file);
+
+  nout << "Sending " << _p3d_temp_file_current_size
+       << " preliminary bytes of " << _p3d_temp_file_total_size
+       << " total p3d data\n";
+        
+  static const size_t buffer_size = 4096;
+  char buffer[buffer_size];
+
+  _p3d_temp_file.close();
+
+  ifstream in(_p3d_temp_filename.c_str(), ios::binary);
+  in.read(buffer, buffer_size);
+  size_t total = 0;
+  size_t count = in.gcount();
+  while (count != 0) {
+    P3D_instance_feed_url_stream(_p3d_inst, _p3d_instance_id,
+                                 P3D_RC_in_progress, 0,
+                                 _p3d_temp_file_total_size, buffer, count);
+    total += count;
+
+    in.read(buffer, buffer_size);
+    count = in.gcount();
+  }
+  nout << "sent " << count << " bytes.\n";
+
+  in.close();
+  _opened_p3d_temp_file = false;
+  unlink(_p3d_temp_filename.c_str());
+  _p3d_temp_filename.clear();
+
+  if (_finished_p3d_temp_file) {
+    // If we'd already finished the stream earlier, tell the plugin.
+    P3D_instance_feed_url_stream(_p3d_inst, _p3d_instance_id,
+                                 P3D_RC_done, 0, _p3d_temp_file_total_size,
+                                 NULL, 0);
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PPInstance::get_core_api
 //       Access: Private
@@ -1376,32 +1514,46 @@ create_instance() {
   _started = true;
   _p3d_inst = P3D_new_instance(request_ready, tokens, _tokens.size(), 
                                0, NULL, this);
+  if (_p3d_inst == NULL) {
+    set_failed();
+    return;
+  }
 
-  if (_p3d_inst != NULL) {
-    // Now get the browser's toplevel DOM object (called the "window"
-    // object in JavaScript), to pass to the plugin.
-    NPObject *window_object = NULL;
-    if (browser->getvalue(_npp_instance, NPNVWindowNPObject,
-                          &window_object) == NPERR_NO_ERROR) {
-      PPBrowserObject *pobj = new PPBrowserObject(this, window_object);
-      P3D_instance_set_browser_script_object(_p3d_inst, pobj);
-      browser->releaseobject(window_object);
-    } else {
-      nout << "Couldn't get window_object\n";
-    }
-
-    if (_script_object != NULL) {
-      // Now that we have a true instance, initialize our
-      // script_object with the proper P3D_object pointer.
-      P3D_object *main = P3D_instance_get_panda_script_object(_p3d_inst);
-      nout << "new instance, setting main = " << main << "\n";
-      _script_object->set_main(main);
-    }
-
-    if (_got_window) {
-      send_window();
+  // Now get the browser's toplevel DOM object (called the "window"
+  // object in JavaScript), to pass to the plugin.
+  NPObject *window_object = NULL;
+  if (browser->getvalue(_npp_instance, NPNVWindowNPObject,
+                        &window_object) == NPERR_NO_ERROR) {
+    PPBrowserObject *pobj = new PPBrowserObject(this, window_object);
+    P3D_instance_set_browser_script_object(_p3d_inst, pobj);
+    browser->releaseobject(window_object);
+  } else {
+    nout << "Couldn't get window_object\n";
+  }
+  
+  if (_script_object != NULL) {
+    // Now that we have a true instance, initialize our
+    // script_object with the proper P3D_object pointer.
+    P3D_object *main = P3D_instance_get_panda_script_object(_p3d_inst);
+    nout << "new instance, setting main = " << main << "\n";
+    _script_object->set_main(main);
+  }
+
+  if (_got_instance_url) {
+    // Create the user_id for streaming the p3d data into the instance.
+    _p3d_instance_id = P3D_instance_start_stream(_p3d_inst, _instance_url.c_str());
+    nout << "p3d instance to stream " << _p3d_instance_id << "\n";
+
+    // If we have already started to receive any instance data, send it
+    // to the plugin now.
+    if (_opened_p3d_temp_file) {
+      send_p3d_temp_file_data();
     }
   }
+  
+  if (_got_window) {
+    send_window();
+  }
 }
 
   
@@ -1865,6 +2017,14 @@ make_ansi_string(wstring &result, NPNSString *ns_string) {
 void PPInstance::
 timer_callback(CFRunLoopTimerRef timer, void *info) {
   PPInstance *self = (PPInstance *)info;
+  ACQUIRE_LOCK(self->_timer_lock);
+  if (self->_request_timer != NULL) {
+    CFRunLoopTimerInvalidate(self->_request_timer);
+    CFRelease(self->_request_timer);
+    self->_request_timer = NULL;
+  }
+  RELEASE_LOCK(self->_timer_lock);
+
   self->handle_request_loop();
 }
 #endif  // __APPLE__

+ 11 - 0
direct/src/plugin_npapi/ppInstance.h

@@ -81,6 +81,9 @@ private:
   static string get_filename_from_url(const string &url);
   void feed_file(PPDownloadRequest *req, const string &filename);
 
+  void open_p3d_temp_file();
+  void send_p3d_temp_file_data();
+
   bool read_contents_file(const string &contents_filename);
   void get_core_api(TiXmlElement *xpackage);
   void downloaded_plugin(const string &filename);
@@ -142,6 +145,13 @@ private:
 
   bool _got_instance_url;
   string _instance_url;
+  bool _opened_p3d_temp_file;
+  bool _finished_p3d_temp_file;
+  size_t _p3d_temp_file_current_size;
+  size_t _p3d_temp_file_total_size;
+  ofstream _p3d_temp_file;
+  string _p3d_temp_filename;
+  int _p3d_instance_id;
  
   // We need to keep a list of the NPStream objects that the instance
   // owns, because Safari (at least) won't automatically delete all of
@@ -189,6 +199,7 @@ private:
 #ifdef __APPLE__
   CFRunLoopRef _run_loop_main;
   CFRunLoopTimerRef _request_timer;
+  LOCK _timer_lock;
 #endif  // __APPLE__
 
   bool _python_window_open;