David Rose %!s(int64=16) %!d(string=hai) anos
pai
achega
f5e2e89d18

+ 1 - 0
direct/src/plugin_npapi/nppanda3d_common.h

@@ -38,6 +38,7 @@ extern ostream *nout_stream;
 #define nout (*nout_stream)
 
 extern string global_root_dir;
+extern bool has_plugin_thread_async_call;
 
 #ifdef _WIN32
 

+ 161 - 60
direct/src/plugin_npapi/ppInstance.cxx

@@ -20,6 +20,7 @@
 #include "p3d_plugin_config.h"
 #include "find_root_dir.h"
 #include "mkdir_complete.h"
+#include "nppanda3d_common.h"
 
 // We can include this header file to get the DTOOL_PLATFORM
 // definition, even though we don't link with dtool.
@@ -53,6 +54,7 @@ PPInstance(NPMIMEType pluginType, NPP instance, uint16_t mode,
   _npp_instance = instance;
   _npp_mode = mode;
   _script_object = NULL;
+  _failed = false;
 
   // Copy the tokens and save them within this object.
   _tokens.reserve(argc);
@@ -93,6 +95,7 @@ PPInstance::
   }
 
   assert(_streams.empty());
+  assert(_file_datas.empty());
 
   if (_script_object != NULL) {
     browser->releaseobject(_script_object);
@@ -119,7 +122,17 @@ PPInstance::
 ////////////////////////////////////////////////////////////////////
 void PPInstance::
 begin() {
-  if (!is_plugin_loaded()) {
+  // On Windows and Linux, we must insist on having this call.  OSX
+  // doesn't necessarily require it.
+#ifndef __APPLE__
+  if (!has_plugin_thread_async_call) {
+    nout << "Browser version insufficient: we require at least NPAPI version 0.19.\n";
+    set_failed();
+    return;
+  }
+#endif  // __APPLE__
+
+  if (!is_plugin_loaded() && !_failed) {
     // Go download the contents file, so we can download the core DLL.
     string url = PANDA_PACKAGE_HOST_URL;
     if (!url.empty() && url[url.length() - 1] != '/') {
@@ -149,6 +162,10 @@ begin() {
 ////////////////////////////////////////////////////////////////////
 void PPInstance::
 set_window(NPWindow *window) {
+  if (_failed) {
+    return;
+  }
+
   if (_got_window && 
       window->x == _window.x &&
       window->y == _window.y &&
@@ -200,6 +217,9 @@ set_window(NPWindow *window) {
 NPError PPInstance::
 new_stream(NPMIMEType type, NPStream *stream, bool seekable, uint16_t *stype) {
   assert(find(_streams.begin(), _streams.end(), stream) == _streams.end());
+  if (_failed) {
+    return NPERR_GENERIC_ERROR;
+  }
 
   if (stream->notifyData == NULL) {
     // This is an unsolicited stream.  Assume the first unsolicited
@@ -272,6 +292,14 @@ stop_outstanding_streams() {
   }
 
   assert(_streams.empty());
+
+  // Also stop any currently pending _file_datas; these are
+  // locally-implemented streams.
+  FileDatas::iterator fi;
+  for (fi = _file_datas.begin(); fi != _file_datas.end(); ++fi) {
+    delete (*fi);
+  }
+  _file_datas.clear();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -282,7 +310,7 @@ stop_outstanding_streams() {
 ////////////////////////////////////////////////////////////////////
 int32_t PPInstance::
 write_ready(NPStream *stream) {
-  if (stream->notifyData != NULL) {
+  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
@@ -325,6 +353,12 @@ write_stream(NPStream *stream, int offset, int len, void *buffer) {
     return 0;
   }
 
+  if (_failed) {
+    // We're done; stop this.
+    browser->destroystream(_npp_instance, stream, NPRES_USER_BREAK);
+    return 0;
+  }
+
   PPDownloadRequest *req = (PPDownloadRequest *)(stream->notifyData);
   switch (req->_rtype) {
   case PPDownloadRequest::RT_user:
@@ -411,6 +445,12 @@ url_notify(const char *url, NPReason reason, void *notifyData) {
   }
   
   PPDownloadRequest *req = (PPDownloadRequest *)notifyData;
+  if (_failed) {
+    // We're done; ignore this.
+    delete req;
+    return;
+  }
+
   switch (req->_rtype) {
   case PPDownloadRequest::RT_user:
     if (!req->_notified_done) {
@@ -546,6 +586,9 @@ stream_as_file(NPStream *stream, const char *fname) {
 void PPInstance::
 handle_request(P3D_request *request) {
   assert(request->_instance == _p3d_inst);
+  if (_failed) {
+    return;
+  }
 
   bool handled = false;
 
@@ -609,12 +652,12 @@ handle_request(P3D_request *request) {
 ////////////////////////////////////////////////////////////////////
 void PPInstance::
 generic_browser_call() {
-  //#ifndef 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();
-  //#endif
+  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();
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -763,6 +806,33 @@ variant_to_p3dobj(const NPVariant *variant) {
   return P3D_new_none_object();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PPInstance::output_np_variant
+//       Access: Public
+//  Description: Outputs the variant value.
+////////////////////////////////////////////////////////////////////
+void PPInstance::
+output_np_variant(ostream &out, const NPVariant &result) {
+  if (NPVARIANT_IS_NULL(result)) {
+    out << "null";
+  } else if (NPVARIANT_IS_VOID(result)) {
+    out << "void";
+  } else if (NPVARIANT_IS_BOOLEAN(result)) {
+    out << "bool " << NPVARIANT_TO_BOOLEAN(result);
+  } else if (NPVARIANT_IS_INT32(result)) {
+    out << "int " << NPVARIANT_TO_INT32(result);
+  } else if (NPVARIANT_IS_DOUBLE(result)) {
+    out << "double " << NPVARIANT_TO_DOUBLE(result);
+  } else if (NPVARIANT_IS_STRING(result)) {
+    NPString str = NPVARIANT_TO_STRING(result);
+    const UC_NPString &uc_str = *(UC_NPString *)(&str);
+    out << "string " << string(uc_str.UTF8Characters, uc_str.UTF8Length);
+  } else if (NPVARIANT_IS_OBJECT(result)) {
+    NPObject *child = NPVARIANT_TO_OBJECT(result);
+    out << "object " << child;
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PPInstance::find_host
 //       Access: Private
@@ -891,36 +961,33 @@ request_ready(P3D_instance *instance) {
   PPInstance *inst = (PPInstance *)(instance->_user_data);
   assert(inst != NULL);
 
+  if (has_plugin_thread_async_call) {
 #ifdef HAS_PLUGIN_THREAD_ASYNC_CALL
-  // Since we are running at least Gecko 1.9, and we have this very
-  // useful function, let's use it to ask the browser to call us back
-  // in the main thread.
-  //  nout << "async: " << (void *)browser->pluginthreadasynccall << "\n";
-  if ((void *)browser->pluginthreadasynccall != (void *)NULL) {
-    browser->pluginthreadasynccall(inst->_npp_instance, browser_sync_callback, NULL);
-  }
-#else  // HAS_PLUGIN_THREAD_ASYNC_CALL
+    // Since we are running at least Gecko 1.9, and we have this very
+    // useful function, let's use it to ask the browser to call us back
+    // in the main thread.
+    if ((void *)browser->pluginthreadasynccall != (void *)NULL) {
+      browser->pluginthreadasynccall(inst->_npp_instance, browser_sync_callback, NULL);
+    }
+#endif  // HAS_PLUGIN_THREAD_ASYNC_CALL
 
-  // If we're using an older version of Gecko, we have to do this some
-  // other, OS-dependent way.
+  } else {
+    // If we're using an older version of Gecko, we have to do this
+    // some other, OS-dependent way.
 
 #ifdef _WIN32
-  // Use a Windows message to forward this event to the main thread.
-
-  // Get the window handle for the window associated with this
-  // instance.
-  const NPWindow *win = inst->get_window();
-  if (win != NULL && win->type == NPWindowTypeWindow) {
-    PostMessage((HWND)(win->window), WM_USER, 0, 0);
-  }
-
-#else
-  // On Mac and Linux, we ignore this asynchronous event, and rely on
-  // detecting it within HandleEvent() and similar callbacks.
-
+    // Use a Windows message to forward this event to the main thread.
+    
+    // Get the window handle for the window associated with this
+    // instance.
+    const NPWindow *win = inst->get_window();
+    if (win != NULL && win->type == NPWindowTypeWindow) {
+      PostMessage((HWND)(win->window), WM_USER, 0, 0);
+    }
 #endif  // _WIN32
-
-#endif  // HAS_PLUGIN_THREAD_ASYNC_CALL
+    // On Mac and Linux, we ignore this asynchronous event, and rely on
+    // detecting it within HandleEvent() and similar callbacks.
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1211,6 +1278,7 @@ do_load_plugin() {
   nout << "Attempting to load core API from " << pathname << "\n";
   if (!load_plugin(pathname, "", "", true, "", "", "", false, false, nout)) {
     nout << "Unable to launch core API in " << pathname << "\n";
+    set_failed();
     return;
   }
 
@@ -1436,29 +1504,62 @@ copy_file(const string &from_filename, const string &to_filename) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: PPInstance::output_np_variant
-//       Access: Public
-//  Description: Outputs the variant value.
+//     Function: PPInstance::set_failed
+//       Access: Private
+//  Description: Called when something has gone wrong that prevents
+//               the plugin instance from running.  Specifically, this
+//               means it failed to load the core API.
 ////////////////////////////////////////////////////////////////////
 void PPInstance::
-output_np_variant(ostream &out, const NPVariant &result) {
-  if (NPVARIANT_IS_NULL(result)) {
-    out << "null";
-  } else if (NPVARIANT_IS_VOID(result)) {
-    out << "void";
-  } else if (NPVARIANT_IS_BOOLEAN(result)) {
-    out << "bool " << NPVARIANT_TO_BOOLEAN(result);
-  } else if (NPVARIANT_IS_INT32(result)) {
-    out << "int " << NPVARIANT_TO_INT32(result);
-  } else if (NPVARIANT_IS_DOUBLE(result)) {
-    out << "double " << NPVARIANT_TO_DOUBLE(result);
-  } else if (NPVARIANT_IS_STRING(result)) {
-    NPString str = NPVARIANT_TO_STRING(result);
-    const UC_NPString &uc_str = *(UC_NPString *)(&str);
-    out << "string " << string(uc_str.UTF8Characters, uc_str.UTF8Length);
-  } else if (NPVARIANT_IS_OBJECT(result)) {
-    NPObject *child = NPVARIANT_TO_OBJECT(result);
-    out << "object " << child;
+set_failed() {
+  if (!_failed) {
+    _failed = true;
+
+    nout << "Plugin failed.\n";
+    stop_outstanding_streams();
+
+    string expression;
+    // Look for the "onpluginfail" token.
+    Tokens::iterator ti;
+    for (ti = _tokens.begin(); ti != _tokens.end(); ++ti) {
+      if ((*ti)._keyword != NULL && (*ti)._value != NULL) {
+        // Make the token lowercase, since HTML is case-insensitive but
+        // we're not.
+        string keyword;
+        for (const char *p = (*ti)._keyword; *p; ++p) {
+          keyword += tolower(*p);
+        }
+        if (keyword == "onpluginfail") {
+          expression = (*ti)._value;
+          break;
+        }
+      }
+    }
+
+    if (!expression.empty()) {
+      // Now attempt to evaluate the expression.
+      NPObject *window_object = NULL;
+      if (browser->getvalue(_npp_instance, NPNVWindowNPObject,
+                            &window_object) == NPERR_NO_ERROR) {
+        NPString npexpr = { expression.c_str(), expression.length() };
+        NPVariant result;
+        if (browser->evaluate(_npp_instance, window_object, 
+                              &npexpr, &result)) {
+          nout << "Eval " << expression << "\n";
+          browser->releasevariantvalue(&result);
+        } else {
+          nout << "Unable to eval " << expression << "\n";
+        }
+        
+        browser->releaseobject(window_object);
+      }
+    }
+
+    if (_p3d_inst != NULL) {
+      P3D_instance_finish(_p3d_inst);
+      _p3d_inst = NULL;
+    }
+    cleanup_window();
   }
 }
 
@@ -1528,12 +1629,12 @@ browser_sync_callback(void *) {
 ////////////////////////////////////////////////////////////////////
 LONG PPInstance::
 window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
-#ifndef HAS_PLUGIN_THREAD_ASYNC_CALL
-  // Since we're here in the main thread, call handle_request_loop()
-  // to see if there are any new requests to be serviced by the main
-  // thread.
-  handle_request_loop();
-#endif
+  if (!has_plugin_thread_async_call) {
+    // Since we're here in the main thread, call handle_request_loop()
+    // to see if there are any new requests to be serviced by the main
+    // thread.
+    handle_request_loop();
+  }
 
   switch (msg) {
   case WM_ERASEBKGND:

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

@@ -89,6 +89,8 @@ private:
   void cleanup_window();
   bool copy_file(const string &from_filename, const string &to_filename);
 
+  void set_failed();
+
   static void handle_request_loop();
   static void browser_sync_callback(void *);
 
@@ -114,6 +116,7 @@ private:
   CoreUrls _core_urls;
 
   FileSpec _core_api_dll;
+  bool _failed;
 
   bool _got_instance_url;
   string _instance_url;

+ 5 - 4
direct/src/plugin_npapi/startup.cxx

@@ -26,6 +26,7 @@ static ofstream logfile;
 ostream *nout_stream = &logfile;
 
 string global_root_dir;
+bool has_plugin_thread_async_call;
 
 NPNetscapeFuncs *browser;
 
@@ -186,11 +187,11 @@ NP_Initialize(NPNetscapeFuncs *browserFuncs,
   nout << "Plugin compiled with version "
        << expected_major << "." << expected_minor << "\n";
 
+  has_plugin_thread_async_call = false;
 #ifdef HAS_PLUGIN_THREAD_ASYNC_CALL
-  // We expect to find at least version NPVERS_HAS_PLUGIN_THREAD_ASYNC_CALL.
-  if (browser_major == 0 && browser_minor < NPVERS_HAS_PLUGIN_THREAD_ASYNC_CALL) {
-    nout << "Cannot run: unsupported version of NPAPI detected.\n";
-    return NPERR_GENERIC_ERROR;
+  // Check if the browser offers this very useful call.
+  if (browser_major > 0 || browser_minor >= NPVERS_HAS_PLUGIN_THREAD_ASYNC_CALL) {
+    has_plugin_thread_async_call = true;
   }
 #endif