diff options
Diffstat (limited to 'nsplugins/nspluginloader.cpp')
-rw-r--r-- | nsplugins/nspluginloader.cpp | 528 |
1 files changed, 528 insertions, 0 deletions
diff --git a/nsplugins/nspluginloader.cpp b/nsplugins/nspluginloader.cpp new file mode 100644 index 000000000..91afa150d --- /dev/null +++ b/nsplugins/nspluginloader.cpp @@ -0,0 +1,528 @@ +/* + + This is an encapsulation of the Netscape plugin API. + + + Copyright (c) 2000 Matthias Hoelzer-Kluepfel <hoelzer@kde.org> + Stefan Schimanski <1Stein@gmx.de> + Copyright (c) 2002-2005 George Staikos <staikos@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + + +#include <qdir.h> + + +#include <kapplication.h> +#include <kprocess.h> +#include <kdebug.h> +#include <kglobal.h> +#include <klocale.h> +#include <kstandarddirs.h> +#include <kconfig.h> +#include <dcopclient.h> +#include <dcopstub.h> +#include <qlayout.h> +#include <qobject.h> +#include <qpushbutton.h> +#include <qxembed.h> +#include <qtextstream.h> +#include <qtimer.h> +#include <qregexp.h> + +#include "nspluginloader.h" +#include "nspluginloader.moc" + +#include "NSPluginClassIface_stub.h" + +#include <config.h> + +NSPluginLoader *NSPluginLoader::s_instance = 0; +int NSPluginLoader::s_refCount = 0; + + +NSPluginInstance::NSPluginInstance(QWidget *parent) + : EMBEDCLASS(parent), _loader( NULL ), shown( false ), inited( false ), resize_count( 0 ), stub( NULL ) +{ +} + +void NSPluginInstance::init(const QCString& app, const QCString& obj) +{ + stub = new NSPluginInstanceIface_stub( app, obj ); + QGridLayout *_layout = new QGridLayout(this, 1, 1); + KConfig cfg("kcmnspluginrc", false); + cfg.setGroup("Misc"); + if (cfg.readBoolEntry("demandLoad", false)) { + _button = new QPushButton(i18n("Start Plugin"), dynamic_cast<EMBEDCLASS*>(this)); + _layout->addWidget(_button, 0, 0); + connect(_button, SIGNAL(clicked()), this, SLOT(loadPlugin())); + show(); + } else { + _button = 0L; + // Protection against repeated NPSetWindow() - Flash v9,0,115,0 doesn't handle + // repeated NPSetWindow() calls properly, which happens when NSPluginInstance is first + // shown and then resized. Which is what happens with KHTML. Therefore use 'shown' + // to detect whether the widget is shown and drop all resize events before that, + // and use 'resize_count' to wait for that one more resize to come (plus a timer + // for a possible timeout). Only then flash is actually initialized ('inited' is true). + resize_count = 1; + QTimer::singleShot( 1000, this, SLOT( doLoadPlugin())); + } +} + +void NSPluginInstance::loadPlugin() +{ + delete _button; + _button = 0; + doLoadPlugin(); +} + +void NSPluginInstance::doLoadPlugin() { + if (!inited && !_button) { + _loader = NSPluginLoader::instance(); + setBackgroundMode(QWidget::NoBackground); + WId winid = stub->winId(); + if( winid != 0 ) { + setProtocol(QXEmbed::XPLAIN); + embed( winid ); + } else { + setProtocol(QXEmbed::XEMBED); + } + // resize before showing, some plugins are stupid and can't handle repeated + // NPSetWindow() calls very well (viewer will avoid the call if not shown yet) + resizePlugin(width(), height()); + displayPlugin(); + show(); + inited = true; + } +} + + +NSPluginInstance::~NSPluginInstance() +{ + kdDebug() << "-> NSPluginInstance::~NSPluginInstance" << endl; + if( inited ) + shutdown(); + kdDebug() << "release" << endl; + if(_loader) + _loader->release(); + kdDebug() << "<- NSPluginInstance::~NSPluginInstance" << endl; + delete stub; +} + + +void NSPluginInstance::windowChanged(WId w) +{ + setBackgroundMode(w == 0 ? QWidget::PaletteBackground : QWidget::NoBackground); + if (w == 0) { + // FIXME: Put a notice here to tell the user that it crashed. + repaint(); + } +} + + +void NSPluginInstance::resizeEvent(QResizeEvent *event) +{ + if (shown == false) // ignore all resizes before being shown + return; + if( !inited && resize_count > 0 ) { + if( --resize_count == 0 ) + doLoadPlugin(); + else + return; + } + EMBEDCLASS::resizeEvent(event); + if (isVisible()) { + resizePlugin(width(), height()); + } + kdDebug() << "NSPluginInstance(client)::resizeEvent" << endl; +} + +void NSPluginInstance::showEvent(QShowEvent *event) +{ + EMBEDCLASS::showEvent(event); + shown = true; + if(!inited && resize_count == 0 ) + doLoadPlugin(); + if(inited) + resizePlugin(width(), height()); +} + +void NSPluginInstance::focusInEvent( QFocusEvent* event ) +{ + stub->gotFocusIn(); +} + +void NSPluginInstance::focusOutEvent( QFocusEvent* event ) +{ + stub->gotFocusOut(); +} + +void NSPluginInstance::displayPlugin() +{ + qApp->syncX(); // process pending X commands + stub->displayPlugin(); +} + +void NSPluginInstance::resizePlugin( int w, int h ) +{ + qApp->syncX(); + stub->resizePlugin( w, h ); +} + +void NSPluginInstance::shutdown() +{ + if( stub ) + stub->shutdown(); +} + +/*******************************************************************************/ + + +NSPluginLoader::NSPluginLoader() + : QObject(), _mapping(7, false), _viewer(0) +{ + scanPlugins(); + _mapping.setAutoDelete( true ); + _filetype.setAutoDelete(true); + + // trap dcop register events + kapp->dcopClient()->setNotifications(true); + QObject::connect(kapp->dcopClient(), + SIGNAL(applicationRegistered(const QCString&)), + this, SLOT(applicationRegistered(const QCString&))); + + // load configuration + KConfig cfg("kcmnspluginrc", false); + cfg.setGroup("Misc"); + _useArtsdsp = cfg.readBoolEntry( "useArtsdsp", false ); +} + + +NSPluginLoader *NSPluginLoader::instance() +{ + if (!s_instance) + s_instance = new NSPluginLoader; + + s_refCount++; + kdDebug() << "NSPluginLoader::instance -> " << s_refCount << endl; + + return s_instance; +} + + +void NSPluginLoader::release() +{ + s_refCount--; + kdDebug() << "NSPluginLoader::release -> " << s_refCount << endl; + + if (s_refCount==0) + { + delete s_instance; + s_instance = 0; + } +} + + +NSPluginLoader::~NSPluginLoader() +{ + kdDebug() << "-> NSPluginLoader::~NSPluginLoader" << endl; + unloadViewer(); + kdDebug() << "<- NSPluginLoader::~NSPluginLoader" << endl; +} + + +void NSPluginLoader::scanPlugins() +{ + QRegExp version(";version=[^:]*:"); + + // open the cache file + QFile cachef(locate("data", "nsplugins/cache")); + if (!cachef.open(IO_ReadOnly)) { + kdDebug() << "Could not load plugin cache file!" << endl; + return; + } + + QTextStream cache(&cachef); + + // read in cache + QString line, plugin; + while (!cache.atEnd()) { + line = cache.readLine(); + if (line.isEmpty() || (line.left(1) == "#")) + continue; + + if (line.left(1) == "[") + { + plugin = line.mid(1,line.length()-2); + continue; + } + + QStringList desc = QStringList::split(':', line, TRUE); + QString mime = desc[0].stripWhiteSpace(); + QStringList suffixes = QStringList::split(',', desc[1].stripWhiteSpace()); + if (!mime.isEmpty()) + { + // insert the mimetype -> plugin mapping + _mapping.insert(mime, new QString(plugin)); + + // insert the suffix -> mimetype mapping + QStringList::Iterator suffix; + for (suffix = suffixes.begin(); suffix != suffixes.end(); ++suffix) { + + // strip whitspaces and any preceding '.' + QString stripped = (*suffix).stripWhiteSpace(); + + unsigned p=0; + for ( ; p<stripped.length() && stripped[p]=='.'; p++ ); + stripped = stripped.right( stripped.length()-p ); + + // add filetype to list + if ( !stripped.isEmpty() && !_filetype.find(stripped) ) + _filetype.insert( stripped, new QString(mime)); + } + } + } +} + + +QString NSPluginLoader::lookupMimeType(const QString &url) +{ + QDictIterator<QString> dit2(_filetype); + while (dit2.current()) + { + QString ext = QString(".")+dit2.currentKey(); + if (url.right(ext.length()) == ext) + return *dit2.current(); + ++dit2; + } + return QString::null; +} + + +QString NSPluginLoader::lookup(const QString &mimeType) +{ + QString plugin; + if ( _mapping[mimeType] ) + plugin = *_mapping[mimeType]; + + kdDebug() << "Looking up plugin for mimetype " << mimeType << ": " << plugin << endl; + + return plugin; +} + + +bool NSPluginLoader::loadViewer() +{ + kdDebug() << "NSPluginLoader::loadViewer" << endl; + + _running = false; + _process = new KProcess; + + // get the dcop app id + int pid = (int)getpid(); + _dcopid.sprintf("nspluginviewer-%d", pid); + + connect( _process, SIGNAL(processExited(KProcess*)), + this, SLOT(processTerminated(KProcess*)) ); + + // find the external viewer process + QString viewer = KGlobal::dirs()->findExe("nspluginviewer"); + if (!viewer) + { + kdDebug() << "can't find nspluginviewer" << endl; + delete _process; + return false; + } + + // find the external artsdsp process + if( _useArtsdsp ) { + kdDebug() << "trying to use artsdsp" << endl; + QString artsdsp = KGlobal::dirs()->findExe("artsdsp"); + if (!artsdsp) + { + kdDebug() << "can't find artsdsp" << endl; + } else + { + kdDebug() << artsdsp << endl; + *_process << artsdsp; + } + } else + kdDebug() << "don't using artsdsp" << endl; + + *_process << viewer; + + // tell the process it's parameters + *_process << "-dcopid"; + *_process << _dcopid; + + // run the process + kdDebug() << "Running nspluginviewer" << endl; + _process->start(); + + // wait for the process to run + int cnt = 0; + while (!kapp->dcopClient()->isApplicationRegistered(_dcopid)) + { + //kapp->processEvents(); // would lead to recursive calls in khtml +#ifdef HAVE_USLEEP + usleep( 50*1000 ); +#else + sleep(1); kdDebug() << "sleep" << endl; +#endif + cnt++; +#ifdef HAVE_USLEEP + if (cnt >= 100) +#else + if (cnt >= 10) +#endif + { + kdDebug() << "timeout" << endl; + delete _process; + return false; + } + + if (!_process->isRunning()) + { + kdDebug() << "nspluginviewer terminated" << endl; + delete _process; + return false; + } + } + + // get viewer dcop interface + _viewer = new NSPluginViewerIface_stub( _dcopid, "viewer" ); + + return _viewer!=0; +} + + +void NSPluginLoader::unloadViewer() +{ + kdDebug() << "-> NSPluginLoader::unloadViewer" << endl; + + if ( _viewer ) + { + _viewer->shutdown(); + kdDebug() << "Shutdown viewer" << endl; + delete _viewer; + delete _process; + _viewer = 0; + _process = 0; + } + + kdDebug() << "<- NSPluginLoader::unloadViewer" << endl; +} + + +void NSPluginLoader::applicationRegistered( const QCString& appId ) +{ + kdDebug() << "DCOP application " << appId.data() << " just registered!" << endl; + + if ( _dcopid==appId ) + { + _running = true; + kdDebug() << "plugin now running" << endl; + } +} + + +void NSPluginLoader::processTerminated(KProcess *proc) +{ + if ( _process == proc) + { + kdDebug() << "Viewer process terminated" << endl; + delete _viewer; + delete _process; + _viewer = 0; + _process = 0; + } +} + + +NSPluginInstance *NSPluginLoader::newInstance(QWidget *parent, QString url, + QString mimeType, bool embed, + QStringList argn, QStringList argv, + QString appId, QString callbackId, bool reload, bool doPost, QByteArray postData) +{ + kdDebug() << "-> NSPluginLoader::NewInstance( parent=" << (void*)parent << ", url=" << url << ", mime=" << mimeType << ", ...)" << endl; + + if ( !_viewer ) + { + // load plugin viewer process + loadViewer(); + + if ( !_viewer ) + { + kdDebug() << "No viewer dcop stub found" << endl; + return 0; + } + } + + // check the mime type + QString mime = mimeType; + if (mime.isEmpty()) + { + mime = lookupMimeType( url ); + argn << "MIME"; + argv << mime; + } + if (mime.isEmpty()) + { + kdDebug() << "Unknown MimeType" << endl; + return 0; + } + + // lookup plugin for mime type + QString plugin_name = lookup(mime); + if (plugin_name.isEmpty()) + { + kdDebug() << "No suitable plugin" << endl; + return 0; + } + + // get plugin class object + DCOPRef cls_ref = _viewer->newClass( plugin_name ); + if ( cls_ref.isNull() ) + { + kdDebug() << "Couldn't create plugin class" << endl; + return 0; + } + NSPluginClassIface_stub *cls = new NSPluginClassIface_stub( cls_ref.app(), cls_ref.object() ); + + // handle special plugin cases + if ( mime=="application/x-shockwave-flash" ) + embed = true; // flash doesn't work in full mode :( + + NSPluginInstance *plugin = new NSPluginInstance( parent ); + kdDebug() << "<- NSPluginLoader::NewInstance = " << (void*)plugin << endl; + + // get plugin instance + DCOPRef inst_ref = cls->newInstance( url, mime, embed, argn, argv, appId, callbackId, reload, doPost, postData, plugin->winId()); + if ( inst_ref.isNull() ) + { + kdDebug() << "Couldn't create plugin instance" << endl; + delete plugin; + return 0; + } + + plugin->init( inst_ref.app(), inst_ref.object() ); + + return plugin; +} + +// vim: ts=4 sw=4 et |