/* This is an encapsulation of the Netscape plugin API. Copyright (c) 2000 Matthias Hoelzer-Kluepfel Stefan Schimanski <1Stein@gmx.de> Copyright (c) 2002-2005 George Staikos 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "nspluginloader.h" #include "nspluginloader.moc" #include "NSPluginClassIface_stub.h" #include 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(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 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