fix safari crash

This commit is contained in:
David Rose 2009-10-23 16:27:47 +00:00
parent c8c51528bd
commit 7e565c2a49
8 changed files with 130 additions and 21 deletions

View File

@ -73,6 +73,18 @@ get_download_success() const {
return _status == P3D_RC_done; return _status == P3D_RC_done;
} }
////////////////////////////////////////////////////////////////////
// Function: P3DDownload::get_download_terminated
// Access: Public
// Description: Returns true if the download has failed because the
// instance is about to be shut down, or false if it
// hasn't failed, or failed for some other reason.
////////////////////////////////////////////////////////////////////
inline bool P3DDownload::
get_download_terminated() const {
return _status == P3D_RC_shutdown;
}
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: P3DDownload::set_download_id // Function: P3DDownload::set_download_id
// Access: Public // Access: Public

View File

@ -40,6 +40,7 @@ public:
inline double get_download_progress() const; inline double get_download_progress() const;
inline bool get_download_finished() const; inline bool get_download_finished() const;
inline bool get_download_success() const; inline bool get_download_success() const;
inline bool get_download_terminated() const;
inline size_t get_total_data() const; inline size_t get_total_data() const;
void cancel(); void cancel();

View File

@ -712,7 +712,7 @@ got_desc_file(TiXmlDocument *doc, bool freshly_downloaded) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: P3DPackage::clear_install_plans // Function: P3DPackage::clear_install_plans
// Access: Private // Access: Private
// Description:a Empties _install_plans cleanly. // Description: Empties _install_plans cleanly.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void P3DPackage:: void P3DPackage::
clear_install_plans() { clear_install_plans() {
@ -883,6 +883,12 @@ follow_install_plans(bool download_finished) {
plan_failed = true; plan_failed = true;
break; break;
case IT_terminate:
// All plans have failed.
_install_plans.clear();
report_done(false);
return;
case IT_continue: case IT_continue:
// A callback hook has been attached; we'll come back later. // A callback hook has been attached; we'll come back later.
return; return;
@ -1213,6 +1219,13 @@ download_finished(bool success) {
close_file(); close_file();
if (!success) { if (!success) {
if (get_download_terminated()) {
// Short-circuit the exit.
_try_urls.clear();
resume_download_finished(false);
return;
}
// Maybe it failed because our contents.xml file is out-of-date. // Maybe it failed because our contents.xml file is out-of-date.
// Go try to freshen it. // Go try to freshen it.
bool is_contents_file = (_dtype == DT_contents_file || _dtype == DT_redownload_contents_file); bool is_contents_file = (_dtype == DT_contents_file || _dtype == DT_redownload_contents_file);
@ -1352,6 +1365,10 @@ do_step(bool download_finished) {
if (_download->get_download_success()) { if (_download->get_download_success()) {
// The Download object has already validated the hash. // The Download object has already validated the hash.
return IT_step_complete; return IT_step_complete;
} else if (_download->get_download_terminated()) {
// The download was interrupted because we're shutting down.
// Don't try any other plans.
return IT_terminate;
} else { } else {
// The Download object has already tried all of the mirrors, and // The Download object has already tried all of the mirrors, and
// they all failed. // they all failed.

View File

@ -112,6 +112,7 @@ private:
IT_step_complete, IT_step_complete,
IT_step_failed, IT_step_failed,
IT_continue, IT_continue,
IT_terminate,
}; };
class InstallStep { class InstallStep {

View File

@ -831,6 +831,12 @@ typedef enum {
There may or may not be data associated with this error as well. There may or may not be data associated with this error as well.
However, no more data will be delivered after this call. */ However, no more data will be delivered after this call. */
P3D_RC_http_error, P3D_RC_http_error,
/* The download was interrupted because we're about to shut down the
instance. The instance should not attempt to restart a new
download in response to this. */
P3D_RC_shutdown
} P3D_result_code; } P3D_result_code;
/* This function is used by the host to handle a get_url request, /* This function is used by the host to handle a get_url request,

View File

@ -84,6 +84,8 @@ PPInstance::
_p3d_inst = NULL; _p3d_inst = NULL;
} }
assert(_streams.empty());
if (_script_object != NULL) { if (_script_object != NULL) {
browser->releaseobject(_script_object); browser->releaseobject(_script_object);
} }
@ -187,6 +189,8 @@ set_window(NPWindow *window) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
NPError PPInstance:: NPError PPInstance::
new_stream(NPMIMEType type, NPStream *stream, bool seekable, uint16 *stype) { new_stream(NPMIMEType type, NPStream *stream, bool seekable, uint16 *stype) {
assert(find(_streams.begin(), _streams.end(), stream) == _streams.end());
if (stream->notifyData == NULL) { if (stream->notifyData == NULL) {
// This is an unsolicited stream. Assume the first unsolicited // This is an unsolicited stream. Assume the first unsolicited
// stream we receive is the instance data; any other unsolicited // stream we receive is the instance data; any other unsolicited
@ -198,6 +202,7 @@ new_stream(NPMIMEType type, NPStream *stream, bool seekable, uint16 *stype) {
stream->notifyData = new PPDownloadRequest(PPDownloadRequest::RT_instance_data); stream->notifyData = new PPDownloadRequest(PPDownloadRequest::RT_instance_data);
*stype = NP_NORMAL; *stype = NP_NORMAL;
_streams.push_back(stream);
return NPERR_NO_ERROR; return NPERR_NO_ERROR;
} }
@ -211,18 +216,21 @@ new_stream(NPMIMEType type, NPStream *stream, bool seekable, uint16 *stype) {
// This is the initial contents.xml file. We'll just download // This is the initial contents.xml file. We'll just download
// this directly to a file, since it is small and this is easy. // this directly to a file, since it is small and this is easy.
*stype = NP_ASFILEONLY; *stype = NP_ASFILEONLY;
_streams.push_back(stream);
return NPERR_NO_ERROR; return NPERR_NO_ERROR;
case PPDownloadRequest::RT_core_dll: case PPDownloadRequest::RT_core_dll:
// This is the core API DLL (or dylib or whatever). We want to // This is the core API DLL (or dylib or whatever). We want to
// download this to file for convenience. // download this to file for convenience.
*stype = NP_ASFILEONLY; *stype = NP_ASFILEONLY;
_streams.push_back(stream);
return NPERR_NO_ERROR; return NPERR_NO_ERROR;
case PPDownloadRequest::RT_user: case PPDownloadRequest::RT_user:
// This is a request from the plugin. We'll receive this as a // This is a request from the plugin. We'll receive this as a
// stream. // stream.
*stype = NP_NORMAL; *stype = NP_NORMAL;
_streams.push_back(stream);
return NPERR_NO_ERROR; return NPERR_NO_ERROR;
default: default:
@ -233,6 +241,29 @@ new_stream(NPMIMEType type, NPStream *stream, bool seekable, uint16 *stype) {
return NPERR_GENERIC_ERROR; return NPERR_GENERIC_ERROR;
} }
////////////////////////////////////////////////////////////////////
// Function: PPInstance::stop_outstanding_streams
// Access: Public
// Description: Stops any download streams that are currently active
// on the instance. It is necessary to call this
// explicitly before destroying the instance, at least
// for Safari.
////////////////////////////////////////////////////////////////////
void PPInstance::
stop_outstanding_streams() {
Streams::iterator si;
Streams streams;
streams.swap(_streams);
for (si = streams.begin(); si != streams.end(); ++si) {
NPStream *stream = (*si);
nout << "Stopping stream " << stream->url << "\n";
browser->destroystream(_npp_instance, stream, NPRES_USER_BREAK);
}
assert(_streams.empty());
}
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: PPInstance::write_ready // Function: PPInstance::write_ready
// Access: Public // Access: Public
@ -310,6 +341,13 @@ write_stream(NPStream *stream, int offset, int len, void *buffer) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
NPError PPInstance:: NPError PPInstance::
destroy_stream(NPStream *stream, NPReason reason) { destroy_stream(NPStream *stream, NPReason reason) {
Streams::iterator si = find(_streams.begin(), _streams.end(), stream);
if (si == _streams.end()) {
nout << "Got destroy_stream for unknown stream\n";
} else {
_streams.erase(si);
}
if (stream->notifyData == NULL) { if (stream->notifyData == NULL) {
nout << "Unexpected destroy_stream on " << stream->url << "\n"; nout << "Unexpected destroy_stream on " << stream->url << "\n";
return NPERR_NO_ERROR; return NPERR_NO_ERROR;
@ -321,7 +359,11 @@ destroy_stream(NPStream *stream, NPReason reason) {
{ {
P3D_result_code result_code = P3D_RC_done; P3D_result_code result_code = P3D_RC_done;
if (reason != NPRES_DONE) { if (reason != NPRES_DONE) {
result_code = P3D_RC_generic_error; if (reason == NPRES_USER_BREAK) {
result_code = P3D_RC_shutdown;
} else {
result_code = P3D_RC_generic_error;
}
} }
assert(!req->_notified_done); assert(!req->_notified_done);
P3D_instance_feed_url_stream(_p3d_inst, req->_user_id, P3D_instance_feed_url_stream(_p3d_inst, req->_user_id,
@ -330,6 +372,12 @@ destroy_stream(NPStream *stream, NPReason reason) {
} }
break; 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
// the destroy_stream notification.
break;
default: default:
nout << "Unexpected destroy_stream on " << stream->url << "\n"; nout << "Unexpected destroy_stream on " << stream->url << "\n";
break; break;
@ -372,13 +420,17 @@ url_notify(const char *url, NPReason reason, void *notifyData) {
if (reason != NPRES_DONE) { if (reason != NPRES_DONE) {
nout << "Failure downloading " << url << "\n"; nout << "Failure downloading " << url << "\n";
// Couldn't download a fresh contents.xml for some reason. If if (reason == NPRES_USER_BREAK) {
// there's an outstanding contents.xml file on disk, try to load nout << "Failure due to user break\n";
// that one as a fallback. } else {
string contents_filename = _root_dir + "/contents.xml"; // Couldn't download a fresh contents.xml for some reason. If
if (!read_contents_file(contents_filename)) { // there's an outstanding contents.xml file on disk, try to
nout << "Unable to read contents file " << contents_filename << "\n"; // load that one as a fallback.
// TODO: fail string contents_filename = _root_dir + "/contents.xml";
if (!read_contents_file(contents_filename)) {
nout << "Unable to read contents file " << contents_filename << "\n";
// TODO: fail
}
} }
} }
break; break;
@ -386,13 +438,18 @@ url_notify(const char *url, NPReason reason, void *notifyData) {
case PPDownloadRequest::RT_core_dll: case PPDownloadRequest::RT_core_dll:
if (reason != NPRES_DONE) { if (reason != NPRES_DONE) {
nout << "Failure downloading " << url << "\n"; nout << "Failure downloading " << url << "\n";
// Couldn't download from this mirror. Try the next one.
if (!_core_urls.empty()) {
string url = _core_urls.back();
_core_urls.pop_back();
PPDownloadRequest *req2 = new PPDownloadRequest(PPDownloadRequest::RT_core_dll); if (reason == NPRES_USER_BREAK) {
start_download(url, req2); nout << "Failure due to user break\n";
} else {
// Couldn't download from this mirror. Try the next one.
if (!_core_urls.empty()) {
string url = _core_urls.back();
_core_urls.pop_back();
PPDownloadRequest *req2 = new PPDownloadRequest(PPDownloadRequest::RT_core_dll);
start_download(url, req2);
}
} }
} }
break; break;

View File

@ -46,6 +46,7 @@ public:
void set_window(NPWindow *window); void set_window(NPWindow *window);
NPError new_stream(NPMIMEType type, NPStream *stream, NPError new_stream(NPMIMEType type, NPStream *stream,
bool seekable, uint16 *stype); bool seekable, uint16 *stype);
void stop_outstanding_streams();
int32 write_ready(NPStream *stream); int32 write_ready(NPStream *stream);
int write_stream(NPStream *stream, int offset, int len, void *buffer); int write_stream(NPStream *stream, int offset, int len, void *buffer);
@ -117,6 +118,12 @@ private:
bool _got_instance_url; bool _got_instance_url;
string _instance_url; string _instance_url;
// We need to keep a list of the NPStream objects that the instance
// owns, because Safari (at least) won't automatically delete all of
// the outstanding streams when the instance is destroyed.
typedef vector<NPStream *> Streams;
Streams _streams;
// This class is used for feeding local files (accessed via a // This class is used for feeding local files (accessed via a
// "file://" url) into the core API. // "file://" url) into the core API.
class StreamingFileData { class StreamingFileData {

View File

@ -243,11 +243,12 @@ NP_Shutdown(void) {
NPError NPError
NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode,
int16 argc, char *argn[], char *argv[], NPSavedData *saved) { int16 argc, char *argn[], char *argv[], NPSavedData *saved) {
nout << "new instance\n"; nout << "new instance " << instance << "\n";
PPInstance *inst = new PPInstance(pluginType, instance, mode, PPInstance *inst = new PPInstance(pluginType, instance, mode,
argc, argn, argv, saved); argc, argn, argv, saved);
instance->pdata = inst; instance->pdata = inst;
nout << "new instance->pdata = " << inst << "\n";
// To experiment with a "windowless" plugin, which really means we // To experiment with a "windowless" plugin, which really means we
// create our own window without an intervening window, try this. // create our own window without an intervening window, try this.
@ -267,10 +268,15 @@ NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode,
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
NPError NPError
NPP_Destroy(NPP instance, NPSavedData **save) { NPP_Destroy(NPP instance, NPSavedData **save) {
nout << "destroy instance " << instance << "\n"; nout << "destroy instance " << instance << ", "
<< (PPInstance *)instance->pdata << "\n";
nout << "save = " << (void *)save << "\n"; nout << "save = " << (void *)save << "\n";
// (*save) = NULL; // (*save) = NULL;
delete (PPInstance *)(instance->pdata); PPInstance *inst = (PPInstance *)(instance->pdata);
assert(inst != NULL);
inst->stop_outstanding_streams();
delete inst;
instance->pdata = NULL; instance->pdata = NULL;
return NPERR_NO_ERROR; return NPERR_NO_ERROR;
@ -330,7 +336,8 @@ NPP_DestroyStream(NPP instance, NPStream *stream, NPReason reason) {
<< ", " << stream->end << ", " << stream->end
<< ", notifyData = " << stream->notifyData << ", notifyData = " << stream->notifyData
<< ", reason = " << reason << ", reason = " << reason
<< "\n"; << ", for " << instance
<< ", " << (PPInstance *)(instance->pdata) << "\n";
PPInstance::generic_browser_call(); PPInstance::generic_browser_call();
PPInstance *inst = (PPInstance *)(instance->pdata); PPInstance *inst = (PPInstance *)(instance->pdata);
@ -346,6 +353,7 @@ NPP_DestroyStream(NPP instance, NPStream *stream, NPReason reason) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
int32 int32
NPP_WriteReady(NPP instance, NPStream *stream) { NPP_WriteReady(NPP instance, NPStream *stream) {
// nout << "WriteReady " << stream->url << " for " << instance << ", " << (PPInstance *)(instance->pdata) << "\n";
PPInstance::generic_browser_call(); PPInstance::generic_browser_call();
PPInstance *inst = (PPInstance *)(instance->pdata); PPInstance *inst = (PPInstance *)(instance->pdata);
assert(inst != NULL); assert(inst != NULL);
@ -362,7 +370,7 @@ NPP_WriteReady(NPP instance, NPStream *stream) {
int32 int32
NPP_Write(NPP instance, NPStream *stream, int32 offset, NPP_Write(NPP instance, NPStream *stream, int32 offset,
int32 len, void *buffer) { int32 len, void *buffer) {
// nout << "Write " << stream->url << ", " << len << "\n"; // nout << "Write " << stream->url << ", " << len << " for " << instance << ", " << (PPInstance *)(instance->pdata) << "\n";
PPInstance::generic_browser_call(); PPInstance::generic_browser_call();
PPInstance *inst = (PPInstance *)(instance->pdata); PPInstance *inst = (PPInstance *)(instance->pdata);
assert(inst != NULL); assert(inst != NULL);