diff options
Diffstat (limited to 'nsplugins/viewer')
-rw-r--r-- | nsplugins/viewer/CMakeLists.txt | 35 | ||||
-rw-r--r-- | nsplugins/viewer/Makefile.am | 11 | ||||
-rw-r--r-- | nsplugins/viewer/NSPluginClassIface.h | 83 | ||||
-rw-r--r-- | nsplugins/viewer/glibevents.cpp | 43 | ||||
-rw-r--r-- | nsplugins/viewer/glibevents.h | 41 | ||||
-rw-r--r-- | nsplugins/viewer/nsplugin.cpp | 2002 | ||||
-rw-r--r-- | nsplugins/viewer/nsplugin.h | 344 | ||||
-rw-r--r-- | nsplugins/viewer/qxteventloop.cpp | 472 | ||||
-rw-r--r-- | nsplugins/viewer/qxteventloop.h | 81 | ||||
-rw-r--r-- | nsplugins/viewer/resolve.h | 46 | ||||
-rw-r--r-- | nsplugins/viewer/viewer.cpp | 289 |
11 files changed, 3447 insertions, 0 deletions
diff --git a/nsplugins/viewer/CMakeLists.txt b/nsplugins/viewer/CMakeLists.txt new file mode 100644 index 000000000..e3149cadb --- /dev/null +++ b/nsplugins/viewer/CMakeLists.txt @@ -0,0 +1,35 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/nsplugins + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} + ${GLIB2_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### nspluginviewer (executable) ############### + +tde_add_executable( nspluginviewer AUTOMOC + SOURCES + ../NSPluginCallbackIface.stub NSPluginClassIface.skel + nsplugin.cpp viewer.cpp qxteventloop.cpp + glibevents.cpp + LINK tdeparts-shared tdeio-shared ${GLIB2_LIBRARIES} Xt ${DL_LIBRARIES} + DESTINATION ${BIN_INSTALL_DIR} +) diff --git a/nsplugins/viewer/Makefile.am b/nsplugins/viewer/Makefile.am new file mode 100644 index 000000000..d8597321f --- /dev/null +++ b/nsplugins/viewer/Makefile.am @@ -0,0 +1,11 @@ +INCLUDES = -I$(top_srcdir)/nsplugins -I$(top_builddir)/nsplugins $(all_includes) `pkg-config --cflags glib-2.0` +METASOURCES = AUTO + +bin_PROGRAMS = nspluginviewer + +nspluginviewer_SOURCES = NSPluginCallbackIface.stub NSPluginClassIface.skel \ + nsplugin.cpp viewer.cpp qxteventloop.cpp glibevents.cpp +nspluginviewer_LDFLAGS = $(all_libraries) $(KDE_RPATH) -export-dynamic `pkg-config --libs glib-2.0` +nspluginviewer_LDADD = $(LIB_TDEIO) $(LIB_TDEPARTS) -lXt + +NSPluginCallbackIface_DIR = $(srcdir)/.. diff --git a/nsplugins/viewer/NSPluginClassIface.h b/nsplugins/viewer/NSPluginClassIface.h new file mode 100644 index 000000000..c9051821d --- /dev/null +++ b/nsplugins/viewer/NSPluginClassIface.h @@ -0,0 +1,83 @@ +/* + + Copyright (c) 2000 Matthias Hoelzer-Kluepfel <[email protected]> + Stefan Schimanski <[email protected]> + + 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. + +*/ + + + +#ifndef __NSPluginClassIface_h__ +#define __NSPluginClassIface_h__ + + +#include <tqstringlist.h> +#include <tqcstring.h> +#include <dcopobject.h> +#include <dcopref.h> + + +class NSPluginViewerIface : virtual public DCOPObject +{ + K_DCOP + +k_dcop: + virtual void shutdown() = 0; + virtual DCOPRef newClass(TQString plugin) = 0; +}; + + +class NSPluginClassIface : virtual public DCOPObject +{ + K_DCOP + +k_dcop: + + virtual DCOPRef 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) = 0; + virtual TQString getMIMEDescription() = 0; + +}; + + +class NSPluginInstanceIface : virtual public DCOPObject +{ + K_DCOP + +k_dcop: + + virtual void shutdown() = 0; + + virtual int winId() = 0; + + virtual int setWindow(TQ_INT8 remove=0) = 0; + + virtual void resizePlugin(TQ_INT32 w, TQ_INT32 h) = 0; + + virtual void javascriptResult(TQ_INT32 id, TQString result) = 0; + + virtual void displayPlugin() = 0; + + virtual void gotFocusIn() = 0; + virtual void gotFocusOut() = 0; +}; + + +#endif + diff --git a/nsplugins/viewer/glibevents.cpp b/nsplugins/viewer/glibevents.cpp new file mode 100644 index 000000000..fe059d1ee --- /dev/null +++ b/nsplugins/viewer/glibevents.cpp @@ -0,0 +1,43 @@ +/* + Copyright (c) 2007 Lubos Lunak <[email protected]> + + 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 "glibevents.h" + +#include <tqapplication.h> + +GlibEvents::GlibEvents() + { + g_main_context_ref( g_main_context_default()); + connect( &timer, TQT_SIGNAL( timeout()), TQT_SLOT( process())); + // TODO Poll for now + timer.start( 10 ); + } + +GlibEvents::~GlibEvents() + { + g_main_context_unref( g_main_context_default()); + } + +void GlibEvents::process() + { + while( g_main_context_pending( g_main_context_default())) + g_main_context_iteration( g_main_context_default(), false ); + } + +#include "glibevents.moc" diff --git a/nsplugins/viewer/glibevents.h b/nsplugins/viewer/glibevents.h new file mode 100644 index 000000000..396273a85 --- /dev/null +++ b/nsplugins/viewer/glibevents.h @@ -0,0 +1,41 @@ +/* + Copyright (c) 2007 Lubos Lunak <[email protected]> + + 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. + +*/ + +#ifndef GLIBEVENTS_H +#define GLIBEVENTS_H + +#include <tqwidget.h> +#include <tqtimer.h> + +#include <glib.h> + +class GlibEvents + : public QWidget + { + Q_OBJECT + public: + GlibEvents(); + virtual ~GlibEvents(); + private slots: + void process(); + private: + TQTimer timer; + }; + +#endif diff --git a/nsplugins/viewer/nsplugin.cpp b/nsplugins/viewer/nsplugin.cpp new file mode 100644 index 000000000..4f3cb3094 --- /dev/null +++ b/nsplugins/viewer/nsplugin.cpp @@ -0,0 +1,2002 @@ +/* + + This is an encapsulation of the Netscape plugin API. + + + Copyright (c) 2000 Matthias Hoelzer-Kluepfel <[email protected]> + Stefan Schimanski <[email protected]> + 2003-2005 George Staikos <[email protected]> + + 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 "nsplugin.h" +#include "resolve.h" + +#ifdef Bool +#undef Bool +#endif + +#include <dcopclient.h> +#include <tdeconfig.h> +#include <kdebug.h> +#include <tdeglobal.h> +#include <tdeio/netaccess.h> +#include <klibloader.h> +#include <tdelocale.h> +#include <kprocess.h> +#include <tdeprotocolmanager.h> +#include <kstandarddirs.h> +#include <tdetempfile.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; + TDEProcess p; + p << TDEGlobal::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(TDEProcess::Block); + // Let's only allow the caller to be reloaded, not everything. + //if (_callback) + // _callback->reloadPage(); + p.start(TDEProcess::DontCare); + } else { + p.start(TDEProcess::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); + TDEConfig 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], "__TDEHTML__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 = TQMIN(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 tdeio job + _job = TDEIO::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(TDEIO::Job *, const TQByteArray &)), + TQT_SLOT(data(TDEIO::Job *, const TQByteArray &))); + connect(_job, TQT_SIGNAL(result(TDEIO::Job *)), TQT_SLOT(result(TDEIO::Job *))); + connect(_job, TQT_SIGNAL(totalSize(TDEIO::Job *, TDEIO::filesize_t )), + TQT_SLOT(totalSize(TDEIO::Job *, TDEIO::filesize_t))); + connect(_job, TQT_SIGNAL(mimetype(TDEIO::Job *, const TQString &)), + TQT_SLOT(mimetype(TDEIO::Job *, const TQString &))); + connect(_job, TQT_SIGNAL(redirection(TDEIO::Job *, const KURL&)), + TQT_SLOT(redirection(TDEIO::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 tdeio job + _job = TDEIO::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(TDEIO::Job *, const TQByteArray &)), + TQT_SLOT(data(TDEIO::Job *, const TQByteArray &))); + connect(_job, TQT_SIGNAL(result(TDEIO::Job *)), TQT_SLOT(result(TDEIO::Job *))); + connect(_job, TQT_SIGNAL(totalSize(TDEIO::Job *, TDEIO::filesize_t )), + TQT_SLOT(totalSize(TDEIO::Job *, TDEIO::filesize_t))); + connect(_job, TQT_SIGNAL(mimetype(TDEIO::Job *, const TQString &)), + TQT_SLOT(mimetype(TDEIO::Job *, const TQString &))); + connect(_job, TQT_SIGNAL(redirection(TDEIO::Job *, const KURL&)), + TQT_SLOT(redirection(TDEIO::Job *, const KURL&))); + } + + return false; +} + + +void NSPluginStream::data(TDEIO::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(TDEIO::Job * /*job*/, const KURL& url) +{ + updateURL( url ); +} + +void NSPluginStream::totalSize(TDEIO::Job * job, TDEIO::filesize_t size) +{ + kdDebug(1431) << "NSPluginStream::totalSize - job=" << (void*)job << " size=" << TDEIO::number(size) << endl; + _stream->end = size; +} + +void NSPluginStream::mimetype(TDEIO::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(TDEIO::Job *job) +{ + int err = job->error(); + _job = 0; + finish( err!=0 || error() ); +} + +#include "nsplugin.moc" +// vim: ts=4 sw=4 et diff --git a/nsplugins/viewer/nsplugin.h b/nsplugins/viewer/nsplugin.h new file mode 100644 index 000000000..96bb1da15 --- /dev/null +++ b/nsplugins/viewer/nsplugin.h @@ -0,0 +1,344 @@ +/* + + This is an encapsulation of the Netscape plugin API. + + Copyright (c) 2000 Matthias Hoelzer-Kluepfel <[email protected]> + Stefan Schimanski <[email protected]> + Copyright (c) 2003-2005 George Staikos <[email protected]> + + 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. + +*/ + + +#ifndef __NS_PLUGIN_H__ +#define __NS_PLUGIN_H__ + + +#include <dcopobject.h> +#include "NSPluginClassIface.h" +#include "NSPluginCallbackIface_stub.h" + + +#include <tqobject.h> +#include <tqstring.h> +#include <tqstringlist.h> +#include <tqptrqueue.h> +#include <tqdict.h> +#include <tqmap.h> +#include <tqintdict.h> +#include <tqguardedptr.h> + +#include <tdeparts/browserextension.h> // for URLArgs +#include <tdeio/job.h> + + +#define XP_UNIX +#define MOZ_X11 +#include "sdk/npupp.h" + +typedef char* NP_GetMIMEDescriptionUPP(void); +typedef NPError NP_InitializeUPP(NPNetscapeFuncs*, NPPluginFuncs*); +typedef NPError NP_ShutdownUPP(void); + + +#include <X11/Intrinsic.h> + + +void quitXt(); + +class KLibrary; +class TQTimer; + + +class NSPluginStreamBase : public TQObject +{ +Q_OBJECT +friend class NSPluginInstance; +public: + NSPluginStreamBase( class NSPluginInstance *instance ); + ~NSPluginStreamBase(); + + KURL url() { return _url; } + int pos() { return _pos; } + void stop(); + +signals: + void finished( NSPluginStreamBase *strm ); + +protected: + void finish( bool err ); + bool pump(); + bool error() { return _error; } + void queue( const TQByteArray &data ); + bool create( const TQString& url, const TQString& mimeType, void *notify, bool forceNotify = false ); + int tries() { return _tries; } + void inform( ); + void updateURL( const KURL& newURL ); + + class NSPluginInstance *_instance; + uint16 _streamType; + NPStream *_stream; + void *_notifyData; + KURL _url; + TQString _fileURL; + TQString _mimeType; + TQByteArray _headers; + TQByteArray _data; + class KTempFile *_tempFile; + +private: + int process( const TQByteArray &data, int start ); + + unsigned int _pos; + TQByteArray _queue; + unsigned int _queuePos; + int _tries; + bool _onlyAsFile; + bool _error; + bool _informed; + bool _forceNotify; +}; + + +class NSPluginStream : public NSPluginStreamBase +{ + Q_OBJECT + +public: + NSPluginStream( class NSPluginInstance *instance ); + ~NSPluginStream(); + + bool get(const TQString& url, const TQString& mimeType, void *notifyData, bool reload = false); + bool post(const TQString& url, const TQByteArray& data, const TQString& mimeType, void *notifyData, const KParts::URLArgs& args); + +protected slots: + void data(TDEIO::Job *job, const TQByteArray &data); + void totalSize(TDEIO::Job *job, TDEIO::filesize_t size); + void mimetype(TDEIO::Job * job, const TQString &mimeType); + void result(TDEIO::Job *job); + void redirection(TDEIO::Job *job, const KURL& url); + void resume(); + +protected: + TQGuardedPtr<TDEIO::TransferJob> _job; + TQTimer *_resumeTimer; +}; + + +class NSPluginBufStream : public NSPluginStreamBase +{ + Q_OBJECT + +public: + NSPluginBufStream( class NSPluginInstance *instance ); + ~NSPluginBufStream(); + + bool get( const TQString& url, const TQString& mimeType, const TQByteArray &buf, void *notifyData, bool singleShot=false ); + +protected slots: + void timer(); + +protected: + TQTimer *_timer; + bool _singleShot; +}; + + +class NSPluginInstance : public TQObject, public virtual NSPluginInstanceIface +{ + Q_OBJECT + +public: + + // constructor, destructor + 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=0 ); + ~NSPluginInstance(); + + // DCOP functions + void shutdown(); + int winId() { return _form != 0 ? XtWindow(_form) : 0; } + int setWindow(TQ_INT8 remove=0); + void resizePlugin(TQ_INT32 w, TQ_INT32 h); + void javascriptResult(TQ_INT32 id, TQString result); + void displayPlugin(); + void gotFocusIn(); + void gotFocusOut(); + + // value handling + NPError NPGetValue(NPPVariable variable, void *value); + NPError NPSetValue(NPNVariable variable, void *value); + + // window handling + NPError NPSetWindow(NPWindow *window); + + // stream functions + NPError NPDestroyStream(NPStream *stream, NPReason reason); + NPError NPNewStream(NPMIMEType type, NPStream *stream, NPBool seekable, uint16 *stype); + void NPStreamAsFile(NPStream *stream, const char *fname); + int32 NPWrite(NPStream *stream, int32 offset, int32 len, void *buf); + int32 NPWriteReady(NPStream *stream); + + // URL functions + void NPURLNotify(TQString url, NPReason reason, void *notifyData); + + // Event handling + uint16 HandleEvent(void *event); + + // signal emitters + void emitStatus( const TQString &message); + void requestURL( const TQString &url, const TQString &mime, + const TQString &target, void *notify, bool forceNotify = false, bool reload = false ); + void postURL( const TQString &url, const TQByteArray& data, const TQString &mime, + const TQString &target, void *notify, const KParts::URLArgs& args, bool forceNotify = false ); + + TQString normalizedURL(const TQString& url) const; + +public slots: + void streamFinished( NSPluginStreamBase *strm ); + +private slots: + void timer(); + +private: + friend class NSPluginStreamBase; + + static void forwarder(Widget, XtPointer, XEvent *, Boolean*); + + void destroy(); + + bool _destroyed; + bool _visible; + void addTempFile(KTempFile *tmpFile); + TQPtrList<KTempFile> _tempFiles; + NSPluginCallbackIface_stub *_callback; + TQPtrList<NSPluginStreamBase> _streams; + KLibrary *_handle; + TQTimer *_timer; + + NPP _npp; + NPPluginFuncs _pluginFuncs; + + Widget _area, _form, _toplevel; + WId _xembed_window; + TQString _baseURL; + int _width, _height; + + struct Request + { + // A GET request + Request( const TQString &_url, const TQString &_mime, + const TQString &_target, void *_notify, bool _forceNotify = false, + bool _reload = false) + { url=_url; mime=_mime; target=_target; notify=_notify; post=false; forceNotify = _forceNotify; reload = _reload; } + + // A POST request + Request( const TQString &_url, const TQByteArray& _data, + const TQString &_mime, const TQString &_target, void *_notify, + const KParts::URLArgs& _args, bool _forceNotify = false) + { url=_url; mime=_mime; target=_target; + notify=_notify; post=true; data=_data; args=_args; + forceNotify = _forceNotify; } + + TQString url; + TQString mime; + TQString target; + TQByteArray data; + bool post; + bool forceNotify; + bool reload; + void *notify; + KParts::URLArgs args; + }; + + NPWindow _win; + NPSetWindowCallbackStruct _win_info; + TQPtrQueue<Request> _waitingRequests; + TQMap<int, Request*> _jsrequests; +}; + + +class NSPluginClass : public TQObject, virtual public NSPluginClassIface +{ + Q_OBJECT +public: + + NSPluginClass( const TQString &library, TQObject *parent, const char *name=0 ); + ~NSPluginClass(); + + TQString getMIMEDescription(); + DCOPRef newInstance(TQString url, TQString mimeType, TQ_INT8 embed, + TQStringList argn, TQStringList argv, + TQString appId, TQString callbackId, TQ_INT8 reload, TQ_INT8 post, + TQByteArray postData, TQ_UINT32 xembed ); + void destroyInstance( NSPluginInstance* inst ); + bool error() { return _error; } + + void setApp(const TQCString& app) { _app = app; } + const TQCString& app() const { return _app; } + +protected slots: + void timer(); + +private: + int initialize(); + void shutdown(); + + KLibrary *_handle; + TQString _libname; + bool _constructed; + bool _error; + TQTimer *_timer; + + NP_GetMIMEDescriptionUPP *_NP_GetMIMEDescription; + NP_InitializeUPP *_NP_Initialize; + NP_ShutdownUPP *_NP_Shutdown; + + NPPluginFuncs _pluginFuncs; + NPNetscapeFuncs _nsFuncs; + + TQPtrList<NSPluginInstance> _instances; + TQPtrList<NSPluginInstance> _trash; + + TQCString _app; + + // If plugins use gtk, we call the gtk_init function for them --- + // but only do it once. + static bool s_initedGTK; +}; + + +class NSPluginViewer : public TQObject, virtual public NSPluginViewerIface +{ + Q_OBJECT +public: + NSPluginViewer( TQCString dcopId, TQObject *parent, const char *name=0 ); + virtual ~NSPluginViewer(); + + void shutdown(); + DCOPRef newClass( TQString plugin ); + +private slots: + void appUnregistered(const TQCString& id); + +private: + TQDict<NSPluginClass> _classes; +}; + + +#endif diff --git a/nsplugins/viewer/qxteventloop.cpp b/nsplugins/viewer/qxteventloop.cpp new file mode 100644 index 000000000..1f112ba08 --- /dev/null +++ b/nsplugins/viewer/qxteventloop.cpp @@ -0,0 +1,472 @@ +/**************************************************************************** +** Implementation of TQWidget class +** +** Created : 931031 +** +** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. +** +** This file is part of the Xt extension of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Trolltech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email [email protected] for +** information about Qt Commercial License Agreements. +** See http://www.trolltech.com/qpl/ for QPL licensing information. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact [email protected] if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#include <config.h> + +#include "qxteventloop.h" + +#if TQT_VERSION >= 0x030100 + +#include <tqapplication.h> +#include <tqwidgetintdict.h> +#include <tdeglobal.h> + +// resolve the conflict between X11's FocusIn and TQEvent::FocusIn +const int XFocusOut = FocusOut; +const int XFocusIn = FocusIn; +#undef FocusOut +#undef FocusIn + +const int XKeyPress = KeyPress; +const int XKeyRelease = KeyRelease; +#undef KeyPress +#undef KeyRelease + +Boolean qmotif_event_dispatcher( XEvent *event ); + +class QXtEventLoopPrivate +{ +public: + QXtEventLoopPrivate(); + + void hookMeUp(); + void unhook(); + + XtAppContext appContext, ownContext; + TQMemArray<XtEventDispatchProc> dispatchers; + TQWidgetIntDict mapper; + + TQIntDict<TQSocketNotifier> socknotDict; + bool activate_timers; + XtIntervalId timerid; + + // arguments for Xt display initialization + const char* applicationClass; + XrmOptionDescRec* options; + int numOptions; +}; +static QXtEventLoopPrivate *static_d = 0; +static XEvent* last_xevent = 0; + + +/*! \internal + Redeliver the given XEvent to Xt. + + Rationale: An XEvent handled by Qt does not go through the Xt event + handlers, and the internal state of Xt/Motif widgets will not be + updated. This function should only be used if an event delivered by + Qt to a TQWidget needs to be sent to an Xt/Motif widget. +*/ +bool QXtEventLoop::redeliverEvent( XEvent *event ) +{ + // redeliver the event to Xt, NOT through Qt + if ( static_d->dispatchers[ event->type ]( event ) ) + return TRUE; + return FALSE; +} + + +/*!\internal + */ +XEvent* QXtEventLoop::lastEvent() +{ + return last_xevent; +} + + +QXtEventLoopPrivate::QXtEventLoopPrivate() + : appContext(NULL), ownContext(NULL), + activate_timers(FALSE), timerid(0) +{ +} + +void QXtEventLoopPrivate::hookMeUp() +{ + // worker to plug Qt into Xt (event dispatchers) + // and Xt into Qt (QXtEventLoopEventLoop) + + // ### TODO extensions? + dispatchers.resize( LASTEvent ); + dispatchers.fill( 0 ); + int et; + for ( et = 2; et < LASTEvent; et++ ) + dispatchers[ et ] = + XtSetEventDispatcher( TQPaintDevice::x11AppDisplay(), + et, ::qmotif_event_dispatcher ); +} + +void QXtEventLoopPrivate::unhook() +{ + // unhook Qt from Xt (event dispatchers) + // unhook Xt from Qt? (QXtEventLoopEventLoop) + + // ### TODO extensions? + int et; + for ( et = 2; et < LASTEvent; et++ ) + (void) XtSetEventDispatcher( TQPaintDevice::x11AppDisplay(), + et, dispatchers[ et ] ); + dispatchers.resize( 0 ); + + /* + We cannot destroy the app context here because it closes the X + display, something TQApplication does as well a bit later. + if ( ownContext ) + XtDestroyApplicationContext( ownContext ); + */ + appContext = ownContext = 0; +} + +extern bool tqt_try_modal( TQWidget *, XEvent * ); // defined in qapplication_x11.cpp +Boolean qmotif_event_dispatcher( XEvent *event ) +{ + TQApplication::sendPostedEvents(); + + TQWidgetIntDict *mapper = &static_d->mapper; + TQWidget* qMotif = mapper->find( event->xany.window ); + if ( !qMotif && TQWidget::find( event->xany.window) == 0 ) { + // event is not for Qt, try Xt + Display* dpy = TQPaintDevice::x11AppDisplay(); + Widget w = XtWindowToWidget( dpy, event->xany.window ); + while ( w && ! ( qMotif = mapper->find( XtWindow( w ) ) ) ) { + if ( XtIsShell( w ) ) { + break; + } + w = XtParent( w ); + } + + if ( qMotif && + ( event->type == XKeyPress || event->type == XKeyRelease ) ) { + // remap key events + event->xany.window = qMotif->winId(); + } + } + + last_xevent = event; + bool delivered = ( tqApp->x11ProcessEvent( event ) != -1 ); + last_xevent = 0; + if ( qMotif ) { + switch ( event->type ) { + case EnterNotify: + case LeaveNotify: + event->xcrossing.focus = False; + delivered = FALSE; + break; + case XKeyPress: + case XKeyRelease: + delivered = TRUE; + break; + case XFocusIn: + case XFocusOut: + delivered = FALSE; + break; + default: + delivered = FALSE; + break; + } + } + + if ( delivered ) + return True; + + + if ( TQApplication::activePopupWidget() ) + // we get all events through the popup grabs. discard the event + return True; + + if ( qMotif && TQApplication::activeModalWidget() ) { + if ( !tqt_try_modal(qMotif, event) ) + return True; + + } + + if ( static_d->dispatchers[ event->type ]( event ) ) + // Xt handled the event. + return True; + + return False; +} + + + +/*! + \class QXtEventLoop + \brief The QXtEventLoop class is the core behind the Motif Extension. + + \extension Motif + + QXtEventLoop only provides a few public functions, but is the brains + behind the integration. QXtEventLoop is responsible for initializing + the Xt toolkit and the Xt application context. It does not open a + connection to the X server, this is done by using TQApplication. + + The only member function in QXtEventLoop that depends on an X server + connection is QXtEventLoop::initialize(). QXtEventLoop must be created before + TQApplication. + + Example usage of QXtEventLoop and TQApplication: + + \code + static char *resources[] = { + ... + }; + + int main(int argc, char **argv) + { + QXtEventLoop integrator( "AppClass" ); + XtAppSetFallbackResources( integrator.applicationContext(), + resources ); + TQApplication app( argc, argv ); + + ... + + return app.exec(); + } + \endcode +*/ + +/*! + Creates QXtEventLoop, which allows Qt and Xt/Motif integration. + + If \a context is NULL, QXtEventLoop creates a default application context + itself. The context is accessible through applicationContext(). + + All arguments passed to this function (\a applicationClass, \a + options and \a numOptions) are used to call XtDisplayInitialize() + after TQApplication has been constructed. +*/ + + + +QXtEventLoop::QXtEventLoop( const char *applicationClass, XtAppContext context, XrmOptionDescRec *options , int numOptions) +{ +#if defined(QT_CHECK_STATE) + if ( static_d ) + tqWarning( "QXtEventLoop: should only have one QXtEventLoop instance!" ); +#endif + + d = static_d = new QXtEventLoopPrivate; + XtToolkitInitialize(); + if ( context ) + d->appContext = context; + else + d->ownContext = d->appContext = XtCreateApplicationContext(); + + d->applicationClass = applicationClass; + d->options = options; + d->numOptions = numOptions; +} + + +/*! + Destroys QXtEventLoop. +*/ +QXtEventLoop::~QXtEventLoop() +{ + // d->unhook(); + delete d; +} + +/*! + Returns the application context. +*/ +XtAppContext QXtEventLoop::applicationContext() const +{ + return d->appContext; +} + + +void QXtEventLoop::appStartingUp() +{ + int argc = tqApp->argc(); + XtDisplayInitialize( d->appContext, + TQPaintDevice::x11AppDisplay(), + tqApp->name(), + d->applicationClass, + d->options, + d->numOptions, + &argc, + tqApp->argv() ); + d->hookMeUp(); +} + +void QXtEventLoop::appClosingDown() +{ + d->unhook(); +} + + +/*!\internal + */ +void QXtEventLoop::registerWidget( TQWidget* w ) +{ + if ( !static_d ) + return; + static_d->mapper.insert( w->winId(), w ); +} + + +/*!\internal + */ +void QXtEventLoop::unregisterWidget( TQWidget* w ) +{ + if ( !static_d ) + return; + static_d->mapper.remove( w->winId() ); +} + + +/*! \internal + */ +void qmotif_socknot_handler( XtPointer pointer, int *, XtInputId *id ) +{ + QXtEventLoop *eventloop = (QXtEventLoop *) pointer; + TQSocketNotifier *socknot = static_d->socknotDict.find( *id ); + if ( ! socknot ) // this shouldn't happen + return; + eventloop->setSocketNotifierPending( socknot ); +} + +/*! \reimp + */ +void QXtEventLoop::registerSocketNotifier( TQSocketNotifier *notifier ) +{ + XtInputMask mask; + switch ( notifier->type() ) { + case TQSocketNotifier::Read: + mask = XtInputReadMask; + break; + + case TQSocketNotifier::Write: + mask = XtInputWriteMask; + break; + + case TQSocketNotifier::Exception: + mask = XtInputExceptMask; + break; + + default: + tqWarning( "QXtEventLoopEventLoop: socket notifier has invalid type" ); + return; + } + + XtInputId id = XtAppAddInput( d->appContext, + notifier->socket(), (XtPointer) mask, + qmotif_socknot_handler, this ); + d->socknotDict.insert( id, notifier ); + + TQEventLoop::registerSocketNotifier( notifier ); +} + +/*! \reimp + */ +void QXtEventLoop::unregisterSocketNotifier( TQSocketNotifier *notifier ) +{ + TQIntDictIterator<TQSocketNotifier> it( d->socknotDict ); + while ( it.current() && notifier != it.current() ) + ++it; + if ( ! it.current() ) { + // this shouldn't happen + tqWarning( "QXtEventLoopEventLoop: failed to unregister socket notifier" ); + return; + } + + XtRemoveInput( it.currentKey() ); + d->socknotDict.remove( it.currentKey() ); + + TQEventLoop::unregisterSocketNotifier( notifier ); +} + +/*! \internal + */ +void qmotif_timeout_handler( XtPointer, XtIntervalId * ) +{ + static_d->activate_timers = TRUE; + static_d->timerid = 0; +} + +/*! \reimp + */ +bool QXtEventLoop::processEvents( ProcessEventsFlags flags ) +{ + // Qt uses posted events to do lots of delayed operations, like repaints... these + // need to be delivered before we go to sleep + TQApplication::sendPostedEvents(); + + // make sure we fire off Qt's timers + int ttw = timeToWait(); + if ( d->timerid != 0 ) { + XtRemoveTimeOut( d->timerid ); + } + d->timerid = 0; + if ( ttw != -1 ) { + d->timerid = + XtAppAddTimeOut( d->appContext, ttw, + qmotif_timeout_handler, 0 ); + } + + // get the pending event mask from Xt and process the next event + XtInputMask pendingmask = XtAppPending( d->appContext ); + XtInputMask mask = pendingmask; + if ( pendingmask & XtIMTimer ) { + mask &= ~XtIMTimer; + // zero timers will starve the Xt X event dispatcher... so process + // something *instead* of a timer first... + if ( mask != 0 ) + XtAppProcessEvent( d->appContext, mask ); + // and process a timer afterwards + mask = pendingmask & XtIMTimer; + } + + if ( ( flags & WaitForMore ) ) + XtAppProcessEvent( d->appContext, XtIMAll ); + else + XtAppProcessEvent( d->appContext, mask ); + + int nevents = 0; + if ( ! ( flags & ExcludeSocketNotifiers ) ) + nevents += activateSocketNotifiers(); + + if ( d->activate_timers ) { + nevents += activateTimers(); + } + d->activate_timers = FALSE; + + return ( (flags & WaitForMore) || ( pendingmask != 0 ) || nevents > 0 ); +} + +#include "qxteventloop.moc" + +#endif + diff --git a/nsplugins/viewer/qxteventloop.h b/nsplugins/viewer/qxteventloop.h new file mode 100644 index 000000000..230c8a32c --- /dev/null +++ b/nsplugins/viewer/qxteventloop.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Implementation of TQWidget class +** +** Created : 931031 +** +** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. +** +** This file is part of the xt extension of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Trolltech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email [email protected] for +** information about Qt Commercial License Agreements. +** See http://www.trolltech.com/qpl/ for QPL licensing information. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact [email protected] if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ +#ifndef QXTEVENTLOOP_H +#define QXTEVENTLOOP_H + +#include <tqglobal.h> + +// #if TQT_VERSION >= 0x030100 +#include <tqeventloop.h> + + + +#include <X11/Intrinsic.h> + +class QXtEventLoopPrivate; + +class QXtEventLoop : public TQEventLoop +{ + Q_OBJECT + + +public: + QXtEventLoop( const char *applicationClass, XtAppContext context = NULL, XrmOptionDescRec *options = 0, int numOptions = 0); + ~QXtEventLoop(); + + XtAppContext applicationContext() const; + + void registerSocketNotifier( TQSocketNotifier * ); + void unregisterSocketNotifier( TQSocketNotifier * ); + + static void registerWidget( TQWidget* ); + static void unregisterWidget( TQWidget* ); + static bool redeliverEvent( XEvent *event ); + static XEvent* lastEvent(); + +protected: + bool processEvents( ProcessEventsFlags flags ); + +private: + void appStartingUp(); + void appClosingDown(); + QXtEventLoopPrivate *d; + +}; + +// #endif + +#endif // QMOTIF_H diff --git a/nsplugins/viewer/resolve.h b/nsplugins/viewer/resolve.h new file mode 100644 index 000000000..d3c747ed2 --- /dev/null +++ b/nsplugins/viewer/resolve.h @@ -0,0 +1,46 @@ +/* + Copyright (c) 2000 Matthias Hoelzer-Kluepfel <[email protected]> + + 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. + +*/ + + +#define RESOLVE_RETVAL(fname,error) \ + kdDebug() << "NSPluginInstance::" << endl; \ + \ + if (!_handle) \ + return error; \ + \ + if (!func_ ## fname) \ + func_ ## fname = _handle->symbol("NPP_"#fname); \ + \ + if (!func_ ## fname) \ + { \ + kdDebug() << "Failed: NPP_" << endl; \ + return error; \ + } \ + kdDebug() << "Resolved NPP_" << endl; + + +#define RESOLVE(fname) RESOLVE_RETVAL(fname, NPERR_GENERIC_ERROR) +#define RESOLVE_VOID(fname) RESOLVE_RETVAL(fname, ;) + + +#define CHECK(fname,error) \ + kdDebug() << "Result of " << #fname << ":" << error << endl; \ + return error; + + diff --git a/nsplugins/viewer/viewer.cpp b/nsplugins/viewer/viewer.cpp new file mode 100644 index 000000000..6007e5b0e --- /dev/null +++ b/nsplugins/viewer/viewer.cpp @@ -0,0 +1,289 @@ +/* + + This is a standalone application that executes Netscape plugins. + + + Copyright (c) 2000 Matthias Hoelzer-Kluepfel <[email protected]> + Stefan Schimanski <[email protected]> + + 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 <config.h> + +#include "nsplugin.h" + +#include <dcopclient.h> +#include <tdeapplication.h> +#include <tdecmdlineargs.h> +#include <kdebug.h> +#include <tdeglobal.h> +#include <tdelocale.h> +#include <tdemessagebox.h> +#include <tqptrlist.h> +#include <tqsocketnotifier.h> +#include <stdlib.h> +#include <sys/resource.h> +#include <sys/time.h> +#include <unistd.h> + +#ifdef Bool +#undef Bool +#endif +#include <tdeconfig.h> + +#include "qxteventloop.h" +#include "glibevents.h" + +/** + * Use RLIMIT_DATA on systems that don't define RLIMIT_AS, + * such as FreeBSD 4. + */ + +#ifndef RLIMIT_AS +#define RLIMIT_AS RLIMIT_DATA +#endif + +/** + * The error handler catches all X errors, writes the error + * message to the debug log and continues. + * + * This is done to prevent abortion of the plugin viewer + * in case the plugin does some invalid X operation. + * + */ +static int x_errhandler(Display *dpy, XErrorEvent *error) +{ + char errstr[256]; + XGetErrorText(dpy, error->error_code, errstr, 256); + kdDebug(1430) << "Detected X Error: " << errstr << endl; + return 1; +} + +/* + * As the plugin viewer needs to be a motif application, I give in to + * the "old style" and keep lot's of global vars. :-) + */ + +static TQCString g_dcopId; + +/** + * parseCommandLine - get command line parameters + * + */ +void parseCommandLine(int argc, char *argv[]) +{ + for (int i=0; i<argc; i++) + { + if (!strcmp(argv[i], "-dcopid") && (i+1 < argc)) + { + g_dcopId = argv[i+1]; + i++; + } + } +} + +#if TQT_VERSION < 0x030100 + +static XtAppContext g_appcon; +static bool g_quit = false; + +void quitXt() +{ + g_quit = true; +} + + +/** + * socket notifier handling + * + */ + +struct SocketNot +{ + int fd; + TQObject *obj; + XtInputId id; +}; + +TQPtrList<SocketNot> _notifiers[3]; + +/** + * socketCallback - send event to the socket notifier + * + */ +void socketCallback(void *client_data, int* /*source*/, XtInputId* /*id*/) +{ + kdDebug(1430) << "-> socketCallback( client_data=" << client_data << " )" << endl; + + TQEvent event( TQEvent::SockAct ); + SocketNot *socknot = (SocketNot *)client_data; + kdDebug(1430) << "obj=" << (void*)socknot->obj << endl; + TQApplication::sendEvent( socknot->obj, &event ); + + kdDebug(1430) << "<- socketCallback" << endl; +} + + +/** + * qt_set_socket_handler - redefined internal qt function to register sockets + * The linker looks in the main binary first and finds this implementation before + * the original one in Qt. I hope this works with every dynamic library loader on any OS. + * + */ +extern bool qt_set_socket_handler( int, int, TQObject *, bool ); +bool qt_set_socket_handler( int sockfd, int type, TQObject *obj, bool enable ) +{ + if ( sockfd < 0 || type < 0 || type > 2 || obj == 0 ) { +#if defined(CHECK_RANGE) + tqWarning( "TQSocketNotifier: Internal error" ); +#endif + return FALSE; + } + + XtPointer inpMask = 0; + + switch (type) { + case TQSocketNotifier::Read: inpMask = (XtPointer)XtInputReadMask; break; + case TQSocketNotifier::Write: inpMask = (XtPointer)XtInputWriteMask; break; + case TQSocketNotifier::Exception: inpMask = (XtPointer)XtInputExceptMask; break; + default: return FALSE; + } + + if (enable) { + SocketNot *sn = new SocketNot; + sn->obj = obj; + sn->fd = sockfd; + + if( _notifiers[type].isEmpty() ) { + _notifiers[type].insert( 0, sn ); + } else { + SocketNot *p = _notifiers[type].first(); + while ( p && p->fd > sockfd ) + p = _notifiers[type].next(); + +#if defined(CHECK_STATE) + if ( p && p->fd==sockfd ) { + static const char *t[] = { "read", "write", "exception" }; + tqWarning( "TQSocketNotifier: Multiple socket notifiers for " + "same socket %d and type %s", sockfd, t[type] ); + } +#endif + if ( p ) + _notifiers[type].insert( _notifiers[type].at(), sn ); + else + _notifiers[type].append( sn ); + } + + sn->id = XtAppAddInput( g_appcon, sockfd, inpMask, socketCallback, sn ); + + } else { + + SocketNot *sn = _notifiers[type].first(); + while ( sn && !(sn->obj == obj && sn->fd == sockfd) ) + sn = _notifiers[type].next(); + if ( !sn ) // not found + return FALSE; + + XtRemoveInput( sn->id ); + _notifiers[type].remove(); + } + + return TRUE; +} +#endif + + +int main(int argc, char** argv) +{ + // nspluginviewer is a helper app, it shouldn't do session management at all + setenv( "SESSION_MANAGER", "", 1 ); + + // trap X errors + kdDebug(1430) << "1 - XSetErrorHandler" << endl; + XSetErrorHandler(x_errhandler); + setvbuf( stderr, NULL, _IONBF, 0 ); + + kdDebug(1430) << "2 - parseCommandLine" << endl; + parseCommandLine(argc, argv); + + kdDebug(1430) << "3 - create QXtEventLoop" << endl; + QXtEventLoop integrator( "nspluginviewer" ); + parseCommandLine(argc, argv); + TDELocale::setMainCatalogue("nsplugin"); + + kdDebug(1430) << "4 - create TDEApplication" << endl; + TDEApplication app( argc, argv, "nspluginviewer", true, true, true ); + GlibEvents glibevents; + + { + TDEConfig cfg("kcmnspluginrc", true); + cfg.setGroup("Misc"); + int v = KCLAMP(cfg.readNumEntry("Nice Level", 0), 0, 19); + if (v > 0) { + nice(v); + } + v = cfg.readNumEntry("Max Memory", 0); + if (v > 0) { + rlimit rl; + memset(&rl, 0, sizeof(rl)); + if (0 == getrlimit(RLIMIT_AS, &rl)) { + rl.rlim_cur = kMin(v, int(rl.rlim_max)); + setrlimit(RLIMIT_AS, &rl); + } + } + } + + // initialize the dcop client + kdDebug(1430) << "5 - app.dcopClient" << endl; + DCOPClient *dcop = app.dcopClient(); + if (!dcop->attach()) + { + KMessageBox::error(NULL, + i18n("There was an error connecting to the Desktop " + "communications server. Please make sure that " + "the 'dcopserver' process has been started, and " + "then try again."), + i18n("Error Connecting to DCOP Server")); + exit(1); + } + + kdDebug(1430) << "6 - dcop->registerAs" << endl; + if (g_dcopId != 0) + g_dcopId = dcop->registerAs( g_dcopId, false ); + else + g_dcopId = dcop->registerAs("nspluginviewer"); + + dcop->setNotifications(true); + + // create dcop interface + kdDebug(1430) << "7 - new NSPluginViewer" << endl; + NSPluginViewer *viewer = new NSPluginViewer( "viewer", 0 ); + + // start main loop +#if TQT_VERSION < 0x030100 + kdDebug(1430) << "8 - XtAppProcessEvent" << endl; + while (!g_quit) + XtAppProcessEvent( g_appcon, XtIMAll); +#else + kdDebug(1430) << "8 - app.exec()" << endl; + app.exec(); +#endif + + // delete viewer + delete viewer; +} |