/* This is an encapsulation of the Netscape plugin API. Copyright (c) 2000 Matthias Hoelzer-Kluepfel <hoelzer@kde.org> Stefan Schimanski <1Stein@gmx.de> 2003-2005 George Staikos <staikos@kde.org> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "NSPluginCallbackIface_stub.h" #include <stdlib.h> #include <unistd.h> #include <tqdict.h> #include <tqdir.h> #include <tqfile.h> #include <tqtimer.h> #include "kxt.h" #include "nsplugin.h" #include "resolve.h" #ifdef Bool #undef Bool #endif #include <dcopclient.h> #include <kconfig.h> #include <kdebug.h> #include <kglobal.h> #include <kio/netaccess.h> #include <klibloader.h> #include <klocale.h> #include <kprocess.h> #include <kprotocolmanager.h> #include <kstandarddirs.h> #include <ktempfile.h> #include <kurl.h> #include <X11/Intrinsic.h> #include <X11/Composite.h> #include <X11/Constraint.h> #include <X11/Shell.h> #include <X11/StringDefs.h> // provide these symbols when compiling with gcc 3.x #if defined __GNUC__ && defined __GNUC_MINOR__ # define KDE_GNUC_PREREQ(maj, min) \ ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min)) #else # define KDE_GNUC_PREREQ(maj, min) 0 #endif #if defined(__GNUC__) && KDE_GNUC_PREREQ(3,0) extern "C" void* __builtin_new(size_t s) { return operator new(s); } extern "C" void __builtin_delete(void* p) { operator delete(p); } extern "C" void* __builtin_vec_new(size_t s) { return operator new[](s); } extern "C" void __builtin_vec_delete(void* p) { operator delete[](p); } extern "C" void __pure_virtual() { abort(); } #endif // server side functions ----------------------------------------------------- // allocate memory void *g_NPN_MemAlloc(uint32 size) { void *mem = ::malloc(size); //kdDebug(1431) << "g_NPN_MemAlloc(), size=" << size << " allocated at " << mem << endl; return mem; } // free memory void g_NPN_MemFree(void *ptr) { //kdDebug(1431) << "g_NPN_MemFree() at " << ptr << endl; if (ptr) ::free(ptr); } uint32 g_NPN_MemFlush(uint32 size) { Q_UNUSED(size); //kdDebug(1431) << "g_NPN_MemFlush()" << endl; // MAC OS only.. we don't use this return 0; } // redraw void g_NPN_ForceRedraw(NPP /*instance*/) { // http://devedge.netscape.com/library/manuals/2002/plugin/1.0/npn_api3.html#999401 // FIXME kdDebug(1431) << "g_NPN_ForceRedraw() [unimplemented]" << endl; } // invalidate rect void g_NPN_InvalidateRect(NPP /*instance*/, NPRect* /*invalidRect*/) { // http://devedge.netscape.com/library/manuals/2002/plugin/1.0/npn_api7.html#999503 // FIXME kdDebug(1431) << "g_NPN_InvalidateRect() [unimplemented]" << endl; } // invalidate region void g_NPN_InvalidateRegion(NPP /*instance*/, NPRegion /*invalidRegion*/) { // http://devedge.netscape.com/library/manuals/2002/plugin/1.0/npn_api8.html#999528 // FIXME kdDebug(1431) << "g_NPN_InvalidateRegion() [unimplemented]" << endl; } // get value NPError g_NPN_GetValue(NPP /*instance*/, NPNVariable variable, void *value) { kdDebug(1431) << "g_NPN_GetValue(), variable=" << static_cast<int>(variable) << endl; switch (variable) { case NPNVxDisplay: *(void**)value = tqt_xdisplay(); return NPERR_NO_ERROR; case NPNVxtAppContext: *(void**)value = XtDisplayToApplicationContext(tqt_xdisplay()); return NPERR_NO_ERROR; case NPNVjavascriptEnabledBool: *(bool*)value = true; return NPERR_NO_ERROR; case NPNVasdEnabledBool: // SmartUpdate - we don't do this *(bool*)value = false; return NPERR_NO_ERROR; case NPNVisOfflineBool: // Offline browsing - no thanks *(bool*)value = false; return NPERR_NO_ERROR; case NPNVToolkit: *(NPNToolkitType*)value = NPNVGtk2; return NPERR_NO_ERROR; case NPNVSupportsXEmbedBool: *(bool*)value = true; return NPERR_NO_ERROR; default: return NPERR_INVALID_PARAM; } } NPError g_NPN_DestroyStream(NPP instance, NPStream* stream, NPReason reason) { // FIXME: is this correct? I imagine it is not. (GS) kdDebug(1431) << "g_NPN_DestroyStream()" << endl; NSPluginInstance *inst = (NSPluginInstance*) instance->ndata; inst->streamFinished( (NSPluginStream *)stream->ndata ); switch (reason) { case NPRES_DONE: return NPERR_NO_ERROR; case NPRES_USER_BREAK: // FIXME: notify the user case NPRES_NETWORK_ERR: // FIXME: notify the user default: return NPERR_GENERIC_ERROR; } } NPError g_NPN_RequestRead(NPStream* /*stream*/, NPByteRange* /*rangeList*/) { // http://devedge.netscape.com/library/manuals/2002/plugin/1.0/npn_api16.html#999734 kdDebug(1431) << "g_NPN_RequestRead() [unimplemented]" << endl; // FIXME return NPERR_GENERIC_ERROR; } NPError g_NPN_NewStream(NPP /*instance*/, NPMIMEType /*type*/, const char* /*target*/, NPStream** /*stream*/) { // http://devedge.netscape.com/library/manuals/2002/plugin/1.0/npn_api12.html#999628 kdDebug(1431) << "g_NPN_NewStream() [unimplemented]" << endl; // FIXME // This creates a stream from the plugin to the browser of type "type" to // display in "target" return NPERR_GENERIC_ERROR; } int32 g_NPN_Write(NPP /*instance*/, NPStream* /*stream*/, int32 /*len*/, void* /*buf*/) { // http://devedge.netscape.com/library/manuals/2002/plugin/1.0/npn_api21.html#999859 kdDebug(1431) << "g_NPN_Write() [unimplemented]" << endl; // FIXME return 0; } // URL functions NPError g_NPN_GetURL(NPP instance, const char *url, const char *target) { kdDebug(1431) << "g_NPN_GetURL: url=" << url << " target=" << target << endl; NSPluginInstance *inst = static_cast<NSPluginInstance*>(instance->ndata); if (inst) { inst->requestURL( TQString::fromLatin1(url), TQString::null, TQString::fromLatin1(target), 0 ); } return NPERR_NO_ERROR; } NPError g_NPN_GetURLNotify(NPP instance, const char *url, const char *target, void* notifyData) { kdDebug(1431) << "g_NPN_GetURLNotify: url=" << url << " target=" << target << " inst=" << (void*)instance << endl; NSPluginInstance *inst = static_cast<NSPluginInstance*>(instance->ndata); if (inst) { kdDebug(1431) << "g_NPN_GetURLNotify: ndata=" << (void*)inst << endl; inst->requestURL( TQString::fromLatin1(url), TQString::null, TQString::fromLatin1(target), notifyData, true ); } return NPERR_NO_ERROR; } NPError g_NPN_PostURLNotify(NPP instance, const char* url, const char* target, uint32 len, const char* buf, NPBool file, void* notifyData) { // http://devedge.netscape.com/library/manuals/2002/plugin/1.0/npn_api14.html kdDebug(1431) << "g_NPN_PostURLNotify() [incomplete]" << endl; kdDebug(1431) << "url=[" << url << "] target=[" << target << "]" << endl; TQByteArray postdata; KParts::URLArgs args; if (len == 0) { return NPERR_NO_DATA; } if (file) { // buf is a filename TQFile f(buf); if (!f.open(IO_ReadOnly)) { return NPERR_FILE_NOT_FOUND; } // FIXME: this will not work because we need to strip the header out! postdata = f.readAll(); f.close(); } else { // buf is raw data // First strip out the header const char *previousStart = buf; uint32 l; bool previousCR = true; for (l = 1;; l++) { if (l == len) { break; } if (buf[l-1] == '\n' || (previousCR && buf[l-1] == '\r')) { if (previousCR) { // header is done! if ((buf[l-1] == '\r' && buf[l] == '\n') || (buf[l-1] == '\n' && buf[l] == '\r')) l++; l++; previousStart = &buf[l-1]; break; } TQString thisLine = TQString::fromLatin1(previousStart, &buf[l-1] - previousStart).stripWhiteSpace(); previousStart = &buf[l]; previousCR = true; kdDebug(1431) << "Found header line: [" << thisLine << "]" << endl; if (thisLine.startsWith("Content-Type: ")) { args.setContentType(thisLine); } } else { previousCR = false; } } postdata.duplicate(previousStart, len - l + 1); } kdDebug(1431) << "Post data: " << postdata.size() << " bytes" << endl; #if 0 TQFile f("/tmp/nspostdata"); f.open(IO_WriteOnly); f.writeBlock(postdata); f.close(); #endif if (!target || !*target) { // Send the results of the post to the plugin // (works by default) } else if (!strcmp(target, "_current") || !strcmp(target, "_self") || !strcmp(target, "_top")) { // Unload the plugin, put the results in the frame/window that the // plugin was loaded in // FIXME } else if (!strcmp(target, "_new") || !strcmp(target, "_blank")){ // Open a new browser window and write the results there // FIXME } else { // Write the results to the specified frame // FIXME } NSPluginInstance *inst = static_cast<NSPluginInstance*>(instance->ndata); if (inst && !inst->normalizedURL(TQString::fromLatin1(url)).isNull()) { inst->postURL( TQString::fromLatin1(url), postdata, args.contentType(), TQString::fromLatin1(target), notifyData, args, true ); } else { // Unsupported / insecure return NPERR_INVALID_URL; } return NPERR_NO_ERROR; } NPError g_NPN_PostURL(NPP instance, const char* url, const char* target, uint32 len, const char* buf, NPBool file) { // http://devedge.netscape.com/library/manuals/2002/plugin/1.0/npn_api13.html kdDebug(1431) << "g_NPN_PostURL()" << endl; kdDebug(1431) << "url=[" << url << "] target=[" << target << "]" << endl; TQByteArray postdata; KParts::URLArgs args; if (len == 0) { return NPERR_NO_DATA; } if (file) { // buf is a filename TQFile f(buf); if (!f.open(IO_ReadOnly)) { return NPERR_FILE_NOT_FOUND; } // FIXME: this will not work because we need to strip the header out! postdata = f.readAll(); f.close(); } else { // buf is raw data // First strip out the header const char *previousStart = buf; uint32 l; bool previousCR = true; for (l = 1;; l++) { if (l == len) { break; } if (buf[l-1] == '\n' || (previousCR && buf[l-1] == '\r')) { if (previousCR) { // header is done! if ((buf[l-1] == '\r' && buf[l] == '\n') || (buf[l-1] == '\n' && buf[l] == '\r')) l++; l++; previousStart = &buf[l-1]; break; } TQString thisLine = TQString::fromLatin1(previousStart, &buf[l-1] - previousStart).stripWhiteSpace(); previousStart = &buf[l]; previousCR = true; kdDebug(1431) << "Found header line: [" << thisLine << "]" << endl; if (thisLine.startsWith("Content-Type: ")) { args.setContentType(thisLine); } } else { previousCR = false; } } postdata.duplicate(previousStart, len - l + 1); } kdDebug(1431) << "Post data: " << postdata.size() << " bytes" << endl; #if 0 TQFile f("/tmp/nspostdata"); f.open(IO_WriteOnly); f.writeBlock(postdata); f.close(); #endif if (!target || !*target) { // Send the results of the post to the plugin // (works by default) } else if (!strcmp(target, "_current") || !strcmp(target, "_self") || !strcmp(target, "_top")) { // Unload the plugin, put the results in the frame/window that the // plugin was loaded in // FIXME } else if (!strcmp(target, "_new") || !strcmp(target, "_blank")){ // Open a new browser window and write the results there // FIXME } else { // Write the results to the specified frame // FIXME } NSPluginInstance *inst = static_cast<NSPluginInstance*>(instance->ndata); if (inst && !inst->normalizedURL(TQString::fromLatin1(url)).isNull()) { inst->postURL( TQString::fromLatin1(url), postdata, args.contentType(), TQString::fromLatin1(target), 0L, args, false ); } else { // Unsupported / insecure return NPERR_INVALID_URL; } return NPERR_NO_ERROR; } // display status message void g_NPN_Status(NPP instance, const char *message) { kdDebug(1431) << "g_NPN_Status(): " << message << endl; if (!instance) return; // turn into an instance signal NSPluginInstance *inst = (NSPluginInstance*) instance->ndata; inst->emitStatus(message); } // inquire user agent const char *g_NPN_UserAgent(NPP /*instance*/) { KProtocolManager kpm; TQString agent = kpm.userAgentForHost("nspluginviewer"); kdDebug(1431) << "g_NPN_UserAgent() = " << agent << endl; // flash crashes without Firefox UA agent = "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.10) Gecko/2007101500 Firefox/2.0.0.10"; return agent.latin1(); } // inquire version information void g_NPN_Version(int *plugin_major, int *plugin_minor, int *browser_major, int *browser_minor) { kdDebug(1431) << "g_NPN_Version()" << endl; // FIXME: Use the sensible values *browser_major = NP_VERSION_MAJOR; *browser_minor = NP_VERSION_MINOR; *plugin_major = NP_VERSION_MAJOR; *plugin_minor = NP_VERSION_MINOR; } void g_NPN_ReloadPlugins(NPBool reloadPages) { // http://devedge.netscape.com/library/manuals/2002/plugin/1.0/npn_api15.html#999713 kdDebug(1431) << "g_NPN_ReloadPlugins()" << endl; KProcess p; p << KGlobal::dirs()->findExe("nspluginscan"); if (reloadPages) { // This is the proper way, but it cannot be done because we have no // handle to the caller! How stupid! We cannot force all konqi windows // to reload - that would be evil. //p.start(KProcess::Block); // Let's only allow the caller to be reloaded, not everything. //if (_callback) // _callback->reloadPage(); p.start(KProcess::DontCare); } else { p.start(KProcess::DontCare); } } // JAVA functions JRIEnv *g_NPN_GetJavaEnv() { kdDebug(1431) << "g_NPN_GetJavaEnv() [unimplemented]" << endl; // FIXME - what do these do? I can't find docs, and even Mozilla doesn't // implement them return 0; } jref g_NPN_GetJavaPeer(NPP /*instance*/) { kdDebug(1431) << "g_NPN_GetJavaPeer() [unimplemented]" << endl; // FIXME - what do these do? I can't find docs, and even Mozilla doesn't // implement them return 0; } NPError g_NPN_SetValue(NPP /*instance*/, NPPVariable variable, void* /*value*/) { kdDebug(1431) << "g_NPN_SetValue() [unimplemented]" << endl; switch (variable) { case NPPVpluginWindowBool: // FIXME // If true, the plugin is windowless. If false, it is in a window. case NPPVpluginTransparentBool: // FIXME // If true, the plugin is displayed transparent default: return NPERR_GENERIC_ERROR; } } /******************************************************************/ void NSPluginInstance::forwarder(Widget w, XtPointer cl_data, XEvent * event, Boolean * cont) { Q_UNUSED(w); NSPluginInstance *inst = (NSPluginInstance*)cl_data; *cont = True; if (inst->_form == 0 || event->xkey.window == XtWindow(inst->_form)) return; *cont = False; event->xkey.window = XtWindow(inst->_form); event->xkey.subwindow = None; XtDispatchEvent(event); } NSPluginInstance::NSPluginInstance(NPP privateData, NPPluginFuncs *pluginFuncs, KLibrary *handle, int width, int height, TQString src, TQString /*mime*/, TQString appId, TQString callbackId, bool embed, WId xembed, TQObject *parent, const char* name ) : DCOPObject(), TQObject( parent, name ) { Q_UNUSED(embed); _visible = false; _npp = privateData; _npp->ndata = this; _destroyed = false; _handle = handle; _width = width; _height = height; _tempFiles.setAutoDelete( true ); _streams.setAutoDelete( true ); _waitingRequests.setAutoDelete( true ); _callback = new NSPluginCallbackIface_stub( appId.latin1(), callbackId.latin1() ); _xembed_window = xembed; _toplevel = _form = 0; KURL base(src); base.setFileName( TQString::null ); _baseURL = base.url(); memcpy(&_pluginFuncs, pluginFuncs, sizeof(_pluginFuncs)); _timer = new TQTimer( this ); connect( _timer, TQT_SIGNAL(timeout()), TQT_SLOT(timer()) ); kdDebug(1431) << "NSPluginInstance::NSPluginInstance" << endl; kdDebug(1431) << "pdata = " << _npp->pdata << endl; kdDebug(1431) << "ndata = " << _npp->ndata << endl; if (width == 0) width = 1600; if (height == 0) height = 1200; if( _xembed_window == 0 ) { // create drawing area Arg args[7]; Cardinal nargs = 0; XtSetArg(args[nargs], XtNwidth, width); nargs++; XtSetArg(args[nargs], XtNheight, height); nargs++; XtSetArg(args[nargs], XtNborderWidth, 0); nargs++; String n, c; XtGetApplicationNameAndClass(tqt_xdisplay(), &n, &c); _toplevel = XtAppCreateShell("drawingArea", c, applicationShellWidgetClass, tqt_xdisplay(), args, nargs); // What exactly does widget mapping mean? Without this call the widget isn't // embedded correctly. With it the viewer doesn't show anything in standalone mode. //if (embed) XtSetMappedWhenManaged(_toplevel, False); XtRealizeWidget(_toplevel); // Create form window that is searched for by flash plugin _form = XtVaCreateWidget("form", compositeWidgetClass, _toplevel, NULL); XtSetArg(args[nargs], XtNvisual, TQPaintDevice::x11AppVisual()); nargs++; XtSetArg(args[nargs], XtNdepth, TQPaintDevice::x11AppDepth()); nargs++; XtSetArg(args[nargs], XtNcolormap, TQPaintDevice::x11AppColormap()); nargs++; XtSetValues(_form, args, nargs); XSync(tqt_xdisplay(), false); // From mozilla - not sure if it's needed yet, nor what to use for embedder #if 0 /* this little trick seems to finish initializing the widget */ #if XlibSpecificationRelease >= 6 XtRegisterDrawable(tqt_xdisplay(), embedderid, _toplevel); #else _XtRegisterWindow(embedderid, _toplevel); #endif #endif XtRealizeWidget(_form); XtManageChild(_form); // Register forwarder XtAddEventHandler(_toplevel, (KeyPressMask|KeyReleaseMask), False, forwarder, (XtPointer)this ); XtAddEventHandler(_form, (KeyPressMask|KeyReleaseMask), False, forwarder, (XtPointer)this ); XSync(tqt_xdisplay(), false); } } NSPluginInstance::~NSPluginInstance() { kdDebug(1431) << "-> ~NSPluginInstance" << endl; destroy(); kdDebug(1431) << "<- ~NSPluginInstance" << endl; } void NSPluginInstance::destroy() { if ( !_destroyed ) { kdDebug(1431) << "delete streams" << endl; _waitingRequests.clear(); shutdown(); for( NSPluginStreamBase *s=_streams.first(); s!=0; ) { NSPluginStreamBase *next = _streams.next(); s->stop(); s = next; } _streams.clear(); kdDebug(1431) << "delete callbacks" << endl; delete _callback; _callback = 0; kdDebug(1431) << "destroy plugin" << endl; NPSavedData *saved = 0; // As of 7/31/01, nsplugin crashes when used with Qt // linked with libGL if the destroy function is called. // A patch on that date hacked out the following call. // On 11/17/01, Jeremy White has reenabled this destroy // in a an attempt to better understand why this crash // occurs so that the real problem can be found and solved. // It's possible that a flaw in the SetWindow call // caused the crash and it is now fixed. if ( _pluginFuncs.destroy ) _pluginFuncs.destroy( _npp, &saved ); if (saved && saved->len && saved->buf) g_NPN_MemFree(saved->buf); if (saved) g_NPN_MemFree(saved); if( _form != 0 ) { XtRemoveEventHandler(_form, (KeyPressMask|KeyReleaseMask), False, forwarder, (XtPointer)this); XtRemoveEventHandler(_toplevel, (KeyPressMask|KeyReleaseMask), False, forwarder, (XtPointer)this); XtDestroyWidget(_form); _form = 0; XtDestroyWidget(_toplevel); _toplevel = 0; } if (_npp) { ::free(_npp); // matched with malloc() in newInstance } _destroyed = true; } } void NSPluginInstance::shutdown() { NSPluginClass *cls = dynamic_cast<NSPluginClass*>(parent()); //destroy(); if (cls) { cls->destroyInstance( this ); } } void NSPluginInstance::timer() { if (!_visible) { _timer->start( 100, true ); return; } //_streams.clear(); // start queued requests kdDebug(1431) << "looking for waiting requests" << endl; while ( _waitingRequests.head() ) { kdDebug(1431) << "request found" << endl; Request req( *_waitingRequests.head() ); _waitingRequests.remove(); TQString url; // make absolute url if ( req.url.left(11).lower()=="javascript:" ) url = req.url; else if ( KURL::isRelativeURL(req.url) ) { KURL bu( _baseURL ); KURL absUrl( bu, req.url ); url = absUrl.url(); } else if ( req.url[0]=='/' && KURL(_baseURL).hasHost() ) { KURL absUrl( _baseURL ); absUrl.setPath( req.url ); url = absUrl.url(); } else url = req.url; // non empty target = frame target if ( !req.target.isEmpty()) { if (_callback) { if ( req.post ) { _callback->postURL( url, req.target, req.data, req.mime ); } else { _callback->requestURL( url, req.target ); } if ( req.notify ) { NPURLNotify( req.url, NPRES_DONE, req.notify ); } } } else { if (!url.isEmpty()) { kdDebug(1431) << "Starting new stream " << req.url << endl; if (req.post) { // create stream NSPluginStream *s = new NSPluginStream( this ); connect( s, TQT_SIGNAL(finished(NSPluginStreamBase*)), TQT_SLOT(streamFinished(NSPluginStreamBase*)) ); _streams.append( s ); kdDebug() << "posting to " << url << endl; emitStatus( i18n("Submitting data to %1").arg(url) ); s->post( url, req.data, req.mime, req.notify, req.args ); } else if (url.lower().startsWith("javascript:")){ if (_callback) { static TQ_INT32 _jsrequestid = 0; _jsrequests.insert(_jsrequestid, new Request(req)); _callback->evalJavaScript(_jsrequestid++, url.mid(11)); } else { kdDebug() << "No callback for javascript: url!" << endl; } } else { // create stream NSPluginStream *s = new NSPluginStream( this ); connect( s, TQT_SIGNAL(finished(NSPluginStreamBase*)), TQT_SLOT(streamFinished(NSPluginStreamBase*)) ); _streams.append( s ); kdDebug() << "getting " << url << endl; emitStatus( i18n("Requesting %1").arg(url) ); s->get( url, req.mime, req.notify, req.reload ); } //break; } } } } TQString NSPluginInstance::normalizedURL(const TQString& url) const { KURL bu( _baseURL ); KURL inURL(bu, url); KConfig cfg("kcmnspluginrc", true); cfg.setGroup("Misc"); if (!cfg.readBoolEntry("HTTP URLs Only", false) || inURL.protocol() == "http" || inURL.protocol() == "https" || inURL.protocol() == "javascript") { return inURL.url(); } // Allow: javascript:, http, https, or no protocol (match loading) kdDebug(1431) << "NSPluginInstance::normalizedURL - I don't think so. http or https only!" << endl; return TQString::null; } void NSPluginInstance::requestURL( const TQString &url, const TQString &mime, const TQString &target, void *notify, bool forceNotify, bool reload ) { // Generally this should already be done, but let's be safe for now. TQString nurl = normalizedURL(url); if (nurl.isNull()) { return; } kdDebug(1431) << "NSPluginInstance::requestURL url=" << nurl << " target=" << target << " notify=" << notify << endl; _waitingRequests.enqueue( new Request( nurl, mime, target, notify, forceNotify, reload ) ); _timer->start( 100, true ); } void NSPluginInstance::postURL( const TQString &url, const TQByteArray& data, const TQString &mime, const TQString &target, void *notify, const KParts::URLArgs& args, bool forceNotify ) { // Generally this should already be done, but let's be safe for now. TQString nurl = normalizedURL(url); if (nurl.isNull()) { return; } kdDebug(1431) << "NSPluginInstance::postURL url=" << nurl << " target=" << target << " notify=" << notify << endl; _waitingRequests.enqueue( new Request( nurl, data, mime, target, notify, args, forceNotify) ); _timer->start( 100, true ); } void NSPluginInstance::emitStatus(const TQString &message) { if( _callback ) _callback->statusMessage( message ); } void NSPluginInstance::streamFinished( NSPluginStreamBase* strm ) { kdDebug(1431) << "-> NSPluginInstance::streamFinished" << endl; emitStatus( TQString::null ); _streams.setAutoDelete(false); // Don't delete it yet!! we get called from // its slot! _streams.remove(strm); _streams.setAutoDelete(true); strm->deleteLater(); _timer->start( 100, true ); } int NSPluginInstance::setWindow(TQ_INT8 remove) { if (remove) { NPSetWindow(0); return NPERR_NO_ERROR; } kdDebug(1431) << "-> NSPluginInstance::setWindow" << endl; _win.x = 0; _win.y = 0; _win.height = _height; _win.width = _width; _win.type = NPWindowTypeWindow; // Well, the docu says sometimes, this is only used on the // MAC, but sometimes it says it's always. Who knows... _win.clipRect.top = 0; _win.clipRect.left = 0; _win.clipRect.bottom = _height; _win.clipRect.right = _width; if( _xembed_window ) { _win.window = (void*) _xembed_window; _win_info.type = NP_SETWINDOW; _win_info.display = tqt_xdisplay(); _win_info.visual = DefaultVisualOfScreen(DefaultScreenOfDisplay(tqt_xdisplay())); _win_info.colormap = DefaultColormapOfScreen(DefaultScreenOfDisplay(tqt_xdisplay())); _win_info.depth = DefaultDepthOfScreen(DefaultScreenOfDisplay(tqt_xdisplay())); } else { _win.window = (void*) XtWindow(_form); _win_info.type = NP_SETWINDOW; _win_info.display = XtDisplay(_form); _win_info.visual = DefaultVisualOfScreen(XtScreen(_form)); _win_info.colormap = DefaultColormapOfScreen(XtScreen(_form)); _win_info.depth = DefaultDepthOfScreen(XtScreen(_form)); } kdDebug(1431) << "Window ID = " << _win.window << endl; _win.ws_info = &_win_info; NPError error = NPSetWindow( &_win ); kdDebug(1431) << "<- NSPluginInstance::setWindow = " << error << endl; return error; } static void resizeWidgets(Window w, int width, int height) { Window rroot, parent, *children; unsigned int nchildren = 0; if (XQueryTree(tqt_xdisplay(), w, &rroot, &parent, &children, &nchildren)) { for (unsigned int i = 0; i < nchildren; i++) { XResizeWindow(tqt_xdisplay(), children[i], width, height); } XFree(children); } } void NSPluginInstance::resizePlugin(TQ_INT32 w, TQ_INT32 h) { if (w == _width && h == _height) return; kdDebug(1431) << "-> NSPluginInstance::resizePlugin( w=" << w << ", h=" << h << " ) " << endl; _width = w; _height = h; if( _form != 0 ) { XResizeWindow(tqt_xdisplay(), XtWindow(_form), w, h); XResizeWindow(tqt_xdisplay(), XtWindow(_toplevel), w, h); Arg args[7]; Cardinal nargs = 0; XtSetArg(args[nargs], XtNwidth, _width); nargs++; XtSetArg(args[nargs], XtNheight, _height); nargs++; XtSetArg(args[nargs], XtNvisual, TQPaintDevice::x11AppVisual()); nargs++; XtSetArg(args[nargs], XtNdepth, TQPaintDevice::x11AppDepth()); nargs++; XtSetArg(args[nargs], XtNcolormap, TQPaintDevice::x11AppColormap()); nargs++; XtSetArg(args[nargs], XtNborderWidth, 0); nargs++; XtSetValues(_toplevel, args, nargs); XtSetValues(_form, args, nargs); resizeWidgets(XtWindow(_form), _width, _height); } // If not visible yet, displayWindow() will call setWindow() again anyway, so avoid this. // This also handled plugins that are broken and cannot handle repeated setWindow() calls // very well. if (!_visible) return; setWindow(); kdDebug(1431) << "<- NSPluginInstance::resizePlugin" << endl; } void NSPluginInstance::javascriptResult(TQ_INT32 id, TQString result) { TQMap<int, Request*>::iterator i = _jsrequests.find( id ); if (i != _jsrequests.end()) { Request *req = i.data(); _jsrequests.remove( i ); NSPluginStream *s = new NSPluginStream( this ); connect( s, TQT_SIGNAL(finished(NSPluginStreamBase*)), TQT_SLOT(streamFinished(NSPluginStreamBase*)) ); _streams.append( s ); int len = result.length(); s->create( req->url, TQString("text/plain"), req->notify, req->forceNotify ); kdDebug(1431) << "javascriptResult has been called with: "<<result<<endl; if (len > 0) { TQByteArray data(len + 1); memcpy(data.data(), result.latin1(), len); data[len] = 0; s->process(data, 0); } else { len = 7; // "unknown" TQByteArray data(len + 1); memcpy(data.data(), "unknown", len); data[len] = 0; s->process(data, 0); } s->finish(false); delete req; } } NPError NSPluginInstance::NPGetValue(NPPVariable variable, void *value) { if( value==0 ) { kdDebug() << "FIXME: value==0 in NSPluginInstance::NPGetValue" << endl; return NPERR_GENERIC_ERROR; } if (!_pluginFuncs.getvalue) return NPERR_GENERIC_ERROR; NPError error = _pluginFuncs.getvalue(_npp, variable, value); CHECK(GetValue,error); } NPError NSPluginInstance::NPSetValue(NPNVariable variable, void *value) { if( value==0 ) { kdDebug() << "FIXME: value==0 in NSPluginInstance::NPSetValue" << endl; return NPERR_GENERIC_ERROR; } if (!_pluginFuncs.setvalue) return NPERR_GENERIC_ERROR; NPError error = _pluginFuncs.setvalue(_npp, variable, value); CHECK(SetValue,error); } NPError NSPluginInstance::NPSetWindow(NPWindow *window) { if( window==0 ) { kdDebug() << "FIXME: window==0 in NSPluginInstance::NPSetWindow" << endl; return NPERR_GENERIC_ERROR; } if (!_pluginFuncs.setwindow) return NPERR_GENERIC_ERROR; NPError error = _pluginFuncs.setwindow(_npp, window); CHECK(SetWindow,error); } NPError NSPluginInstance::NPDestroyStream(NPStream *stream, NPReason reason) { if( stream==0 ) { kdDebug() << "FIXME: stream==0 in NSPluginInstance::NPDestroyStream" << endl; return NPERR_GENERIC_ERROR; } if (!_pluginFuncs.destroystream) return NPERR_GENERIC_ERROR; NPError error = _pluginFuncs.destroystream(_npp, stream, reason); CHECK(DestroyStream,error); } NPError NSPluginInstance::NPNewStream(NPMIMEType type, NPStream *stream, NPBool seekable, uint16 *stype) { if( stream==0 ) { kdDebug() << "FIXME: stream==0 in NSPluginInstance::NPNewStream" << endl; return NPERR_GENERIC_ERROR; } if( stype==0 ) { kdDebug() << "FIXME: stype==0 in NSPluginInstance::NPNewStream" << endl; return NPERR_GENERIC_ERROR; } if (!_pluginFuncs.newstream) return NPERR_GENERIC_ERROR; NPError error = _pluginFuncs.newstream(_npp, type, stream, seekable, stype); CHECK(NewStream,error); } void NSPluginInstance::NPStreamAsFile(NPStream *stream, const char *fname) { if( stream==0 ) { kdDebug() << "FIXME: stream==0 in NSPluginInstance::NPStreamAsFile" << endl; return; } if( fname==0 ) { kdDebug() << "FIXME: fname==0 in NSPluginInstance::NPStreamAsFile" << endl; return; } if (!_pluginFuncs.asfile) return; _pluginFuncs.asfile(_npp, stream, fname); } int32 NSPluginInstance::NPWrite(NPStream *stream, int32 offset, int32 len, void *buf) { if( stream==0 ) { kdDebug() << "FIXME: stream==0 in NSPluginInstance::NPWrite" << endl; return 0; } if( buf==0 ) { kdDebug() << "FIXME: buf==0 in NSPluginInstance::NPWrite" << endl; return 0; } if (!_pluginFuncs.write) return 0; return _pluginFuncs.write(_npp, stream, offset, len, buf); } int32 NSPluginInstance::NPWriteReady(NPStream *stream) { if( stream==0 ) { kdDebug() << "FIXME: stream==0 in NSPluginInstance::NPWriteReady" << endl; return 0; } if (!_pluginFuncs.writeready) return 0; return _pluginFuncs.writeready(_npp, stream); } void NSPluginInstance::NPURLNotify(TQString url, NPReason reason, void *notifyData) { if (!_pluginFuncs.urlnotify) return; _pluginFuncs.urlnotify(_npp, url.ascii(), reason, notifyData); } void NSPluginInstance::addTempFile(KTempFile *tmpFile) { _tempFiles.append(tmpFile); } /* * We have to call this after we reparent the widget otherwise some plugins * like the ones based on WINE get very confused. (their coordinates are not * adjusted for the mouse at best) */ void NSPluginInstance::displayPlugin() { // display plugin setWindow(); _visible = true; kdDebug(1431) << "<- NSPluginInstance::displayPlugin = " << (void*)this << endl; } static bool has_focus = false; void NSPluginInstance::gotFocusIn() { has_focus = true; } void NSPluginInstance::gotFocusOut() { has_focus = false; } #include <dlfcn.h> // Prevent plugins from polling the keyboard regardless of focus. static int (*real_xquerykeymap)( Display*, char[32] ) = NULL; extern "C" KDE_EXPORT int XQueryKeymap( Display* dpy, char k[32] ) { if( real_xquerykeymap == NULL ) real_xquerykeymap = (int (*)( Display*, char[32] )) dlsym( RTLD_NEXT, "XQueryKeymap" ); if( has_focus ) return real_xquerykeymap( dpy, k ); memset( k, 0, 32 ); return 1; } /***************************************************************************/ NSPluginViewer::NSPluginViewer( TQCString dcopId, TQObject *parent, const char *name ) : DCOPObject(dcopId), TQObject( parent, name ) { _classes.setAutoDelete( true ); connect(TDEApplication::dcopClient(), TQT_SIGNAL(applicationRemoved(const TQCString&)), this, TQT_SLOT(appUnregistered(const TQCString&))); } NSPluginViewer::~NSPluginViewer() { kdDebug(1431) << "NSPluginViewer::~NSPluginViewer" << endl; } void NSPluginViewer::appUnregistered(const TQCString& id) { if (id.isEmpty()) { return; } TQDictIterator<NSPluginClass> it(_classes); NSPluginClass *c; while ( (c = it.current()) ) { TQString key = it.currentKey(); ++it; if (c->app() == id) { _classes.remove(key); } } if (_classes.isEmpty()) { shutdown(); } } void NSPluginViewer::shutdown() { kdDebug(1431) << "NSPluginViewer::shutdown" << endl; _classes.clear(); #if TQT_VERSION < 0x030100 quitXt(); #else tqApp->quit(); #endif } DCOPRef NSPluginViewer::newClass( TQString plugin ) { kdDebug(1431) << "NSPluginViewer::NewClass( " << plugin << ")" << endl; // search existing class NSPluginClass *cls = _classes[ plugin ]; if ( !cls ) { // create new class cls = new NSPluginClass( plugin, this ); TQCString id = ""; DCOPClient *dc = callingDcopClient(); if (dc) { id = dc->senderId(); } cls->setApp(id); if ( cls->error() ) { kdError(1431) << "Can't create plugin class" << endl; delete cls; return DCOPRef(); } _classes.insert( plugin, cls ); } return DCOPRef( kapp->dcopClient()->appId(), cls->objId() ); } /****************************************************************************/ bool NSPluginClass::s_initedGTK = false; typedef void gtkInitFunc(int *argc, char ***argv); NSPluginClass::NSPluginClass( const TQString &library, TQObject *parent, const char *name ) : DCOPObject(), TQObject( parent, name ) { // initialize members _handle = KLibLoader::self()->library(TQFile::encodeName(library)); _libname = library; _constructed = false; _error = true; _instances.setAutoDelete( true ); _NP_GetMIMEDescription = 0; _NP_Initialize = 0; _NP_Shutdown = 0; _timer = new TQTimer( this ); connect( _timer, TQT_SIGNAL(timeout()), TQT_SLOT(timer()) ); // check lib handle if (!_handle) { kdDebug(1431) << "Could not dlopen " << library << endl; return; } // get exported lib functions _NP_GetMIMEDescription = (NP_GetMIMEDescriptionUPP *)_handle->symbol("NP_GetMIMEDescription"); _NP_Initialize = (NP_InitializeUPP *)_handle->symbol("NP_Initialize"); _NP_Shutdown = (NP_ShutdownUPP *)_handle->symbol("NP_Shutdown"); // check for valid returned ptrs if (!_NP_GetMIMEDescription) { kdDebug(1431) << "Could not get symbol NP_GetMIMEDescription" << endl; return; } if (!_NP_Initialize) { kdDebug(1431) << "Could not get symbol NP_Initialize" << endl; return; } if (!_NP_Shutdown) { kdDebug(1431) << "Could not get symbol NP_Shutdown" << endl; return; } // initialize plugin kdDebug(1431) << "Plugin library " << library << " loaded!" << endl; // see if it uses gtk if (!s_initedGTK) { gtkInitFunc* gtkInit = (gtkInitFunc*)_handle->symbol("gtk_init"); if (gtkInit) { kdDebug(1431) << "Calling gtk_init for the plugin" << endl; // Prevent gtk_init() from replacing the X error handlers, since the Gtk // handlers abort when they receive an X error, thus killing the viewer. int (*old_error_handler)(Display*,XErrorEvent*) = XSetErrorHandler(0); int (*old_io_error_handler)(Display*) = XSetIOErrorHandler(0); gtkInit(0, 0); XSetErrorHandler(old_error_handler); XSetIOErrorHandler(old_io_error_handler); s_initedGTK = true; } } _constructed = true; _error = initialize()!=NPERR_NO_ERROR; } NSPluginClass::~NSPluginClass() { _instances.clear(); _trash.clear(); shutdown(); if (_handle) _handle->unload(); } void NSPluginClass::timer() { // delete instances for ( NSPluginInstance *it=_trash.first(); it!=0; it=_trash.next() ) _instances.remove(it); _trash.clear(); } int NSPluginClass::initialize() { kdDebug(1431) << "NSPluginClass::Initialize()" << endl; if ( !_constructed ) return NPERR_GENERIC_ERROR; // initialize nescape exported functions memset(&_pluginFuncs, 0, sizeof(_pluginFuncs)); memset(&_nsFuncs, 0, sizeof(_nsFuncs)); _pluginFuncs.size = sizeof(_pluginFuncs); _nsFuncs.size = sizeof(_nsFuncs); _nsFuncs.version = (NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR; _nsFuncs.geturl = g_NPN_GetURL; _nsFuncs.posturl = g_NPN_PostURL; _nsFuncs.requestread = g_NPN_RequestRead; _nsFuncs.newstream = g_NPN_NewStream; _nsFuncs.write = g_NPN_Write; _nsFuncs.destroystream = g_NPN_DestroyStream; _nsFuncs.status = g_NPN_Status; _nsFuncs.uagent = g_NPN_UserAgent; _nsFuncs.memalloc = g_NPN_MemAlloc; _nsFuncs.memfree = g_NPN_MemFree; _nsFuncs.memflush = g_NPN_MemFlush; _nsFuncs.reloadplugins = g_NPN_ReloadPlugins; _nsFuncs.getJavaEnv = g_NPN_GetJavaEnv; _nsFuncs.getJavaPeer = g_NPN_GetJavaPeer; _nsFuncs.geturlnotify = g_NPN_GetURLNotify; _nsFuncs.posturlnotify = g_NPN_PostURLNotify; _nsFuncs.getvalue = g_NPN_GetValue; _nsFuncs.setvalue = g_NPN_SetValue; _nsFuncs.invalidaterect = g_NPN_InvalidateRect; _nsFuncs.invalidateregion = g_NPN_InvalidateRegion; _nsFuncs.forceredraw = g_NPN_ForceRedraw; // initialize plugin NPError error = _NP_Initialize(&_nsFuncs, &_pluginFuncs); CHECK(Initialize,error); } TQString NSPluginClass::getMIMEDescription() { return _NP_GetMIMEDescription(); } void NSPluginClass::shutdown() { kdDebug(1431) << "NSPluginClass::shutdown error=" << _error << endl; if( _NP_Shutdown && !_error ) _NP_Shutdown(); } DCOPRef NSPluginClass::newInstance( TQString url, TQString mimeType, TQ_INT8 embed, TQStringList argn, TQStringList argv, TQString appId, TQString callbackId, TQ_INT8 reload, TQ_INT8 doPost, TQByteArray postData, TQ_UINT32 xembed ) { kdDebug(1431) << "-> NSPluginClass::NewInstance" << endl; if ( !_constructed ) return DCOPRef(); // copy parameters over unsigned int argc = argn.count(); char **_argn = new char*[argc]; char **_argv = new char*[argc]; TQString src = url; int width = 0; int height = 0; TQString baseURL = url; for (unsigned int i=0; i<argc; i++) { TQCString encN = argn[i].utf8(); TQCString encV = argv[i].utf8(); const char *n = encN; const char *v = encV; _argn[i] = strdup(n); _argv[i] = strdup(v); if (!strcasecmp(_argn[i], "WIDTH")) width = argv[i].toInt(); if (!strcasecmp(_argn[i], "HEIGHT")) height = argv[i].toInt(); if (!strcasecmp(_argn[i], "__KHTML__PLUGINBASEURL")) baseURL = _argv[i]; kdDebug(1431) << "argn=" << _argn[i] << " argv=" << _argv[i] << endl; } // create plugin instance char mime[256]; strncpy(mime, mimeType.ascii(), 255); mime[255] = 0; NPP npp = (NPP)malloc(sizeof(NPP_t)); // I think we should be using // malloc here, just to be safe, // since the nsplugin plays with // this thing memset(npp, 0, sizeof(NPP_t)); npp->ndata = NULL; // create plugin instance NPError error = _pluginFuncs.newp(mime, npp, embed ? NP_EMBED : NP_FULL, argc, _argn, _argv, 0); kdDebug(1431) << "NPP_New = " << (int)error << endl; // don't use bool here, it can be 1 byte, but some plugins write it as int, and I can't find what the spec says int wants_xembed = false; if (_pluginFuncs.getvalue) { NPError error = _pluginFuncs.getvalue(npp, (NPPVariable)14/*NPPVpluginNeedsXEmbed*/, &wants_xembed ); if( error != NPERR_NO_ERROR ) wants_xembed = false; } kdDebug(1431) << "Plugin requires XEmbed:" << (bool)wants_xembed << endl; // Create plugin instance object NSPluginInstance *inst = new NSPluginInstance( npp, &_pluginFuncs, _handle, width, height, baseURL, mimeType, appId, callbackId, embed, wants_xembed ? xembed : 0, this ); // free arrays with arguments delete [] _argn; delete [] _argv; // check for error if ( error!=NPERR_NO_ERROR) { delete inst; //delete npp; double delete! kdDebug(1431) << "<- PluginClass::NewInstance = 0" << endl; return DCOPRef(); } // create source stream if ( !src.isEmpty() ) { if (doPost) { inst->postURL(src, postData, mimeType, TQString::null, 0, KParts::URLArgs(), false); } else { inst->requestURL( src, mimeType, TQString::null, 0, false, reload ); } } _instances.append( inst ); return DCOPRef(kapp->dcopClient()->appId(), inst->objId()); } void NSPluginClass::destroyInstance( NSPluginInstance* inst ) { // mark for destruction _trash.append( inst ); timer(); //_timer->start( 0, TRUE ); } /****************************************************************************/ NSPluginStreamBase::NSPluginStreamBase( NSPluginInstance *instance ) : TQObject( instance ), _instance(instance), _stream(0), _tempFile(0L), _pos(0), _queue(0), _queuePos(0), _error(false) { _informed = false; } NSPluginStreamBase::~NSPluginStreamBase() { if (_stream) { _instance->NPDestroyStream( _stream, NPRES_USER_BREAK ); if (_stream && _stream->url) free(const_cast<char*>(_stream->url)); delete _stream; _stream = 0; } delete _tempFile; _tempFile = 0; } void NSPluginStreamBase::stop() { finish( true ); } void NSPluginStreamBase::inform() { if (! _informed) { KURL src(_url); _informed = true; // inform the plugin _instance->NPNewStream( _mimeType.isEmpty() ? (char *) "text/plain" : (char*)_mimeType.ascii(), _stream, false, &_streamType ); kdDebug(1431) << "NewStream stype=" << _streamType << " url=" << _url << " mime=" << _mimeType << endl; // prepare data transfer _tempFile = 0L; if ( _streamType==NP_ASFILE || _streamType==NP_ASFILEONLY ) { _onlyAsFile = _streamType==NP_ASFILEONLY; if ( KURL(_url).isLocalFile() ) { kdDebug(1431) << "local file" << endl; // local file can be passed directly _fileURL = KURL(_url).path(); // without streaming stream is finished already if ( _onlyAsFile ) { kdDebug() << "local file AS_FILE_ONLY" << endl; finish( false ); } } else { kdDebug() << "remote file" << endl; // stream into temporary file (use lower() in case the // filename as an upper case X in it) _tempFile = new KTempFile; _tempFile->setAutoDelete( TRUE ); _fileURL = _tempFile->name(); kdDebug() << "saving into " << _fileURL << endl; } } } } bool NSPluginStreamBase::create( const TQString& url, const TQString& mimeType, void *notify, bool forceNotify) { if ( _stream ) return false; _url = url; _notifyData = notify; _pos = 0; _tries = 0; _onlyAsFile = false; _streamType = NP_NORMAL; _informed = false; _forceNotify = forceNotify; // create new stream _stream = new NPStream; _stream->ndata = this; _stream->url = strdup(url.ascii()); _stream->end = 0; _stream->pdata = 0; _stream->lastmodified = 0; _stream->notifyData = _notifyData; _stream->headers = 0; _mimeType = mimeType; return true; } void NSPluginStreamBase::updateURL( const KURL& newURL ) { _url = newURL; free(const_cast<char*>(_stream->url)); _stream->url = strdup(_url.url().ascii()); } int NSPluginStreamBase::process( const TQByteArray &data, int start ) { int32 max, sent, to_sent, len; char *d = const_cast<TQByteArray&>(data).data() + start; to_sent = data.size() - start; while (to_sent > 0) { inform(); max = _instance->NPWriteReady(_stream); //kdDebug(1431) << "to_sent == " << to_sent << " and max = " << max << endl; len = QMIN(max, to_sent); //kdDebug(1431) << "-> Feeding stream to plugin: offset=" << _pos << ", len=" << len << endl; sent = _instance->NPWrite( _stream, _pos, len, d ); //kdDebug(1431) << "<- Feeding stream: sent = " << sent << endl; if (sent == 0) // interrupt the stream for a few ms break; if (sent < 0) { // stream data rejected/error kdDebug(1431) << "stream data rejected/error" << endl; _error = true; break; } if (_tempFile) { _tempFile->dataStream()->writeRawBytes(d, sent); } to_sent -= sent; _pos += sent; d += sent; } return data.size() - to_sent; } bool NSPluginStreamBase::pump() { //kdDebug(1431) << "queue pos " << _queuePos << ", size " << _queue.size() << endl; inform(); if ( _queuePos<_queue.size() ) { unsigned newPos; // handle AS_FILE_ONLY streams if ( _onlyAsFile ) { if (_tempFile) { _tempFile->dataStream()->writeRawBytes( _queue, _queue.size() ); } newPos = _queuePos+_queue.size(); } else { // normal streams newPos = process( _queue, _queuePos ); } // count tries if ( newPos==_queuePos ) _tries++; else _tries = 0; _queuePos = newPos; } // return true if queue finished return _queuePos>=_queue.size(); } void NSPluginStreamBase::queue( const TQByteArray &data ) { _queue = data; _queue.detach(); _queuePos = 0; _tries = 0; /* kdDebug(1431) << "new queue size=" << data.size() << " data=" << (void*)data.data() << " queue=" << (void*)_queue.data() << " qsize=" << _queue.size() << endl; */ } void NSPluginStreamBase::finish( bool err ) { kdDebug(1431) << "finish error=" << err << endl; _queue.resize( 0 ); _pos = 0; _queuePos = 0; inform(); if ( !err ) { if ( _tempFile ) { _tempFile->close(); _instance->addTempFile( _tempFile ); _tempFile = 0; } if ( !_fileURL.isEmpty() ) { kdDebug() << "stream as file " << _fileURL << endl; _instance->NPStreamAsFile( _stream, _fileURL.ascii() ); } _instance->NPDestroyStream( _stream, NPRES_DONE ); if (_notifyData || _forceNotify) _instance->NPURLNotify( _url.url(), NPRES_DONE, _notifyData ); } else { // close temp file if ( _tempFile ) { _tempFile->close(); } // destroy stream _instance->NPDestroyStream( _stream, NPRES_NETWORK_ERR ); if (_notifyData || _forceNotify) _instance->NPURLNotify( _url.url(), NPRES_NETWORK_ERR, _notifyData ); } // delete stream if (_stream && _stream->url) free(const_cast<char *>(_stream->url)); delete _stream; _stream = 0; // destroy NSPluginStream object emit finished( this ); } /****************************************************************************/ NSPluginBufStream::NSPluginBufStream( class NSPluginInstance *instance ) : NSPluginStreamBase( instance ) { _timer = new TQTimer( this ); connect( _timer, TQT_SIGNAL(timeout()), this, TQT_SLOT(timer()) ); } NSPluginBufStream::~NSPluginBufStream() { } bool NSPluginBufStream::get( const TQString& url, const TQString& mimeType, const TQByteArray &buf, void *notifyData, bool singleShot ) { _singleShot = singleShot; if ( create( url, mimeType, notifyData ) ) { queue( buf ); _timer->start( 100, true ); } return false; } void NSPluginBufStream::timer() { bool finished = pump(); if ( _singleShot ) finish( false ); else { if ( !finished && tries()<=8 ) _timer->start( 100, true ); else finish( error() || tries()>8 ); } } /****************************************************************************/ NSPluginStream::NSPluginStream( NSPluginInstance *instance ) : NSPluginStreamBase( instance ), _job(0) { _resumeTimer = new TQTimer( this ); connect(_resumeTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(resume())); } NSPluginStream::~NSPluginStream() { if ( _job ) _job->kill( true ); } bool NSPluginStream::get( const TQString& url, const TQString& mimeType, void *notify, bool reload ) { // create new stream if ( create( url, mimeType, notify ) ) { // start the kio job _job = KIO::get(KURL( url ), false, false); _job->addMetaData("errorPage", "false"); _job->addMetaData("AllowCompressedPage", "false"); _job->addMetaData("PropagateHttpHeader", "true"); if (reload) { _job->addMetaData("cache", "reload"); } connect(_job, TQT_SIGNAL(data(KIO::Job *, const TQByteArray &)), TQT_SLOT(data(KIO::Job *, const TQByteArray &))); connect(_job, TQT_SIGNAL(result(KIO::Job *)), TQT_SLOT(result(KIO::Job *))); connect(_job, TQT_SIGNAL(totalSize(KIO::Job *, KIO::filesize_t )), TQT_SLOT(totalSize(KIO::Job *, KIO::filesize_t))); connect(_job, TQT_SIGNAL(mimetype(KIO::Job *, const TQString &)), TQT_SLOT(mimetype(KIO::Job *, const TQString &))); connect(_job, TQT_SIGNAL(redirection(KIO::Job *, const KURL&)), TQT_SLOT(redirection(KIO::Job *, const KURL&))); } return false; } bool NSPluginStream::post( const TQString& url, const TQByteArray& data, const TQString& mimeType, void *notify, const KParts::URLArgs& args ) { // create new stream if ( create( url, mimeType, notify ) ) { // start the kio job _job = KIO::http_post(KURL( url ), data, false); _job->addMetaData("content-type", args.contentType()); _job->addMetaData("errorPage", "false"); _job->addMetaData("PropagateHttpHeader", "true"); _job->addMetaData("AllowCompressedPage", "false"); connect(_job, TQT_SIGNAL(data(KIO::Job *, const TQByteArray &)), TQT_SLOT(data(KIO::Job *, const TQByteArray &))); connect(_job, TQT_SIGNAL(result(KIO::Job *)), TQT_SLOT(result(KIO::Job *))); connect(_job, TQT_SIGNAL(totalSize(KIO::Job *, KIO::filesize_t )), TQT_SLOT(totalSize(KIO::Job *, KIO::filesize_t))); connect(_job, TQT_SIGNAL(mimetype(KIO::Job *, const TQString &)), TQT_SLOT(mimetype(KIO::Job *, const TQString &))); connect(_job, TQT_SIGNAL(redirection(KIO::Job *, const KURL&)), TQT_SLOT(redirection(KIO::Job *, const KURL&))); } return false; } void NSPluginStream::data(KIO::Job * job, const TQByteArray &data) { //kdDebug(1431) << "NSPluginStream::data - job=" << (void*)job << " data size=" << data.size() << endl; queue( data ); if ( !pump() ) { _job->suspend(); _resumeTimer->start( 100, TRUE ); } } void NSPluginStream::redirection(KIO::Job * /*job*/, const KURL& url) { updateURL( url ); } void NSPluginStream::totalSize(KIO::Job * job, KIO::filesize_t size) { kdDebug(1431) << "NSPluginStream::totalSize - job=" << (void*)job << " size=" << KIO::number(size) << endl; _stream->end = size; } void NSPluginStream::mimetype(KIO::Job * job, const TQString &mimeType) { kdDebug(1431) << "NSPluginStream::mimetype - job=" << (void*)job << " mimeType=" << mimeType << endl; _mimeType = mimeType; TQString tmp_headers = job->metaData()["HTTP-Headers"]; _headers.duplicate(tmp_headers.latin1(), tmp_headers.length()); _stream->headers = _headers.data(); } void NSPluginStream::resume() { if ( error() || tries()>8 ) { _job->kill( true ); finish( true ); return; } if ( pump() ) { kdDebug(1431) << "resume job" << endl; _job->resume(); } else { kdDebug(1431) << "restart timer" << endl; _resumeTimer->start( 100, TRUE ); } } void NSPluginStream::result(KIO::Job *job) { int err = job->error(); _job = 0; finish( err!=0 || error() ); } #include "nsplugin.moc" // vim: ts=4 sw=4 et