onPluginFail

This commit is contained in:
David Rose 2009-11-21 17:37:09 +00:00
parent d60e61993e
commit f5e2e89d18
4 changed files with 170 additions and 64 deletions

View File

@ -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

View File

@ -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:

View File

@ -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;

View File

@ -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