delay a few things on startup, such as setting the visibility mode, which ensures...
[personal-kdebase.git] / apps / nsplugins / nspluginloader.cpp
blob838e6445157177ac1877d9feb7f190dd2ac8742a
1 /*
3 This is an encapsulation of the Netscape plugin API.
6 Copyright (c) 2000 Matthias Hoelzer-Kluepfel <hoelzer@kde.org>
7 Stefan Schimanski <1Stein@gmx.de>
8 Copyright (c) 2002-2005 George Staikos <staikos@kde.org>
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26 #include "nspluginloader.h"
27 #include "nspluginloader.moc"
29 #include <QDir>
30 #include <QGridLayout>
31 #include <QResizeEvent>
34 #include <kapplication.h>
35 #include <kprocess.h>
36 #include <kdebug.h>
37 #include <kglobal.h>
38 #include <klocale.h>
39 #include <kstandarddirs.h>
40 #include <kconfig.h>
41 #include <kconfiggroup.h>
42 #include <QLayout>
43 #include <QObject>
44 #include <QPushButton>
45 #include <QtGui/QX11EmbedContainer>
46 #include <QTextStream>
47 #include <QRegExp>
48 #include <QTimer>
50 #include "nsplugins_class_interface.h"
51 #include "nsplugins_instance_interface.h"
52 #include "nsplugins_viewer_interface.h"
54 #include <config-apps.h>
56 #include <X11/Xlib.h>
57 #include <unistd.h>
59 NSPluginLoader *NSPluginLoader::s_instance = 0;
60 int NSPluginLoader::s_refCount = 0;
63 NSPluginInstance::NSPluginInstance(QWidget *parent, const QString& viewerDBusId, const QString& id, const KUrl& baseUrl)
64 : EMBEDCLASS(parent), _loader(0), inited(false), haveSize(false), _button(0)
66 setWindowTitle("nsp.host"); // for debugging..
67 _instanceInterface = new org::kde::nsplugins::Instance( viewerDBusId, id, QDBusConnection::sessionBus() );
69 QGridLayout *_layout = new QGridLayout(this);
70 _layout->setMargin(1);
71 _layout->setSpacing(1);
72 KConfig _cfg( "kcmnspluginrc" );
73 KConfigGroup cfg(&_cfg, "Misc");
74 if (cfg.readEntry("demandLoad", false)) {
75 KSharedConfigPtr config = KSharedConfig::openConfig("konquerorrc");
76 KConfigGroup settings(config, "Java/JavaScript Settings");
77 if (settings.readEntry("PluginDomains", QStringList()).contains(baseUrl.host())) {
78 KConfigGroup pluginDomains(config, baseUrl.host());
79 if (pluginDomains.readEntry("plugins.EnablePlugins", false)) {
80 return;
83 _button = new QPushButton(i18n("Start Plugin"), this);
84 _layout->addWidget(_button, 0, 0);
85 connect(_button, SIGNAL(clicked()), this, SLOT(loadPlugin()));
86 show();
90 void NSPluginInstance::loadPlugin()
92 delete _button;
93 _button = 0;
94 doLoadPlugin(width(), height());
97 void NSPluginInstance::doLoadPlugin(int w, int h) {
98 if (!inited && !_button) {
99 _loader = NSPluginLoader::instance();
100 // resize before showing, some plugins are stupid and can't handle repeated
101 // NPSetWindow() calls very well (viewer will avoid the call if not shown yet)
102 qApp->syncX();
103 _instanceInterface->setupWindow(winId(), w, h);
104 inited = true;
108 NSPluginInstance::~NSPluginInstance()
110 kDebug() << "-> NSPluginInstance::~NSPluginInstance";
111 _instanceInterface->shutdown();
112 kDebug() << "release";
113 if (_loader)
114 _loader->release();
115 kDebug() << "<- NSPluginInstance::~NSPluginInstance";
119 void NSPluginInstance::windowChanged(WId w)
121 if (w == 0) {
122 // FIXME: Put a notice here to tell the user that it crashed.
123 repaint();
127 void NSPluginInstance::pluginResized(int w, int h)
129 kDebug() << w << h;
130 haveSize = true;
131 embedIfNeeded(w, h);
134 void NSPluginInstance::embedIfNeeded(int w, int h)
136 if (isVisible()) {
137 if (haveSize && !inited)
138 doLoadPlugin(w, h);
139 else if (inited)
140 resizePlugin(w, h);
144 void NSPluginInstance::resizeEvent(QResizeEvent *event)
146 kDebug() << width() << height() << isVisible() << haveSize << inited;
147 EMBEDCLASS::resizeEvent(event);
149 embedIfNeeded(width(), height());
152 void NSPluginInstance::showEvent(QShowEvent *event)
154 kDebug() << width() << height() << isVisible() << haveSize << inited;
155 EMBEDCLASS::showEvent(event);
157 embedIfNeeded(width(), height());
160 void NSPluginInstance::javascriptResult(int id, const QString &result)
162 _instanceInterface->javascriptResult( id, result );
165 void NSPluginInstance::focusInEvent( QFocusEvent* event )
167 Q_UNUSED( event );
168 _instanceInterface->gotFocusIn();
171 void NSPluginInstance::focusOutEvent( QFocusEvent* event )
173 Q_UNUSED( event );
174 _instanceInterface->gotFocusOut();
177 void NSPluginInstance::resizePlugin( int w, int h )
179 qApp->syncX();
180 _instanceInterface->resizePlugin( clientWinId(), w, h );
183 /*******************************************************************************/
186 NSPluginLoader::NSPluginLoader()
187 : QObject(), _mapping(), _viewer(0)
189 scanPlugins();
193 NSPluginLoader *NSPluginLoader::instance()
195 if (!s_instance)
196 s_instance = new NSPluginLoader;
198 s_refCount++;
199 kDebug() << "NSPluginLoader::instance -> " << s_refCount;
201 return s_instance;
205 void NSPluginLoader::release()
207 s_refCount--;
208 kDebug() << "NSPluginLoader::release -> " << s_refCount;
210 if (s_refCount==0)
212 delete s_instance;
213 s_instance = 0;
218 NSPluginLoader::~NSPluginLoader()
220 kDebug() << "-> NSPluginLoader::~NSPluginLoader";
221 unloadViewer();
222 kDebug() << "<- NSPluginLoader::~NSPluginLoader";
226 void NSPluginLoader::scanPlugins()
228 QRegExp version(";version=[^:]*:");
230 // open the cache file
231 QFile cachef(KStandardDirs::locate("data", "nsplugins/cache"));
232 if (!cachef.open(QIODevice::ReadOnly)) {
233 kDebug() << "Could not load plugin cache file!";
234 return;
237 QTextStream cache(&cachef);
239 // read in cache
240 QString line, plugin;
241 while (!cache.atEnd()) {
242 line = cache.readLine();
243 if (line.isEmpty() || (line.left(1) == "#"))
244 continue;
246 if (line.left(1) == "[")
248 plugin = line.mid(1,line.length()-2);
249 continue;
252 QStringList desc = line.split(':', QString::KeepEmptyParts);
253 // avoid crash on broken lines
254 if (desc.size()<2)
255 continue;
257 QString mime = desc[0].trimmed();
258 QStringList suffixes;
259 // If there are no suffixes, this would cause a crash
260 if (desc.count() > 1)
261 suffixes = desc[1].trimmed().split(',');
262 if (!mime.isEmpty())
264 // insert the mimetype -> plugin mapping
265 _mapping.insert(mime, QString(plugin).toLower());
267 // insert the suffix -> mimetype mapping
268 QStringList::Iterator suffix;
269 for (suffix = suffixes.begin(); suffix != suffixes.end(); ++suffix) {
271 // strip whitspaces and any preceding '.'
272 QString stripped = (*suffix).trimmed();
274 int p=0;
275 for ( ; p<stripped.length() && stripped[p]=='.'; p++ ) {
278 stripped = stripped.right( stripped.length()-p );
280 // add filetype to list
281 if ( !stripped.isEmpty() && !_filetype.contains(stripped) )
282 _filetype.insert( stripped, QString(mime));
289 QString NSPluginLoader::lookupMimeType(const QString &url)
291 QString result;
292 QHashIterator<QString, QString> dit2(_filetype);
293 while ( dit2.hasNext() ) {
294 dit2.next();
295 QString ext = QString(".")+dit2.key();
296 if (url.right(ext.length()) == ext) {
297 result = dit2.value();
298 break;
302 return result;
306 QString NSPluginLoader::lookup(const QString &mimeType)
308 QString plugin;
309 if ( _mapping.contains(mimeType) )
310 plugin = _mapping.value(mimeType);
312 kDebug() << "Looking up plugin for mimetype " << mimeType << ": " << plugin;
314 return plugin;
318 bool NSPluginLoader::loadViewer()
320 kDebug() << "NSPluginLoader::loadViewer";
322 _process.clearProgram();
323 // get the dbus app id
324 int pid = (int)getpid();
325 QString tmp;
326 tmp.sprintf("org.kde.nspluginviewer-%d",pid);
327 _viewerDBusId =tmp.toLatin1();
329 connect( &_process, SIGNAL(finished(int, QProcess::ExitStatus)),
330 this, SLOT(processTerminated()) );
332 // find the external viewer process
333 QString viewer = KGlobal::dirs()->findExe("nspluginviewer");
334 if (viewer.isEmpty())
336 kDebug() << "can't find nspluginviewer";
337 return false;
340 _process << viewer;
342 _process << "-dbusservice";
343 _process << _viewerDBusId;
345 // run the process
346 kDebug() << "Running nspluginviewer";
347 _process.start();
349 // wait for the process to run
350 int cnt = 0;
351 while (!QDBusConnection::sessionBus().interface()->isServiceRegistered(_viewerDBusId))
353 //kapp->processEvents(); // would lead to recursive calls in khtml
354 #ifdef HAVE_USLEEP
355 usleep( 50*1000 );
356 #else
357 sleep(1); kDebug() << "sleep";
358 #endif
359 cnt++;
360 #ifdef HAVE_USLEEP
361 if (cnt >= 100)
362 #else
363 if (cnt >= 10)
364 #endif
366 kDebug() << "timeout";
367 _process.kill();
368 return false;
371 if (_process.state() == QProcess::NotRunning)
373 kDebug() << "nspluginviewer terminated";
374 return false;
378 // get viewer dcop interface
379 _viewer = new org::kde::nsplugins::Viewer( _viewerDBusId, "/Viewer", QDBusConnection::sessionBus() );
381 return _viewer!=0;
385 void NSPluginLoader::unloadViewer()
387 kDebug() << "-> NSPluginLoader::unloadViewer";
389 if ( _viewer )
391 _viewer->shutdown();
392 kDebug() << "Shutdown viewer";
393 delete _viewer;
394 _process.kill();
395 _viewer = 0;
398 kDebug() << "<- NSPluginLoader::unloadViewer";
404 void NSPluginLoader::processTerminated()
406 kDebug() << "Viewer process terminated";
407 delete _viewer;
408 _viewer = 0;
412 NSPluginInstance *NSPluginLoader::newInstance(QWidget *parent, const QString& url,
413 const QString& mimeType, bool embed,
414 const QStringList& _argn, const QStringList& _argv,
415 const QString& ownDBusId, const QString& callbackId, bool reload )
417 kDebug() << "-> NSPluginLoader::NewInstance( parent=" << (void*)parent << ", url=" << url << ", mime=" << mimeType << ", " << _argn << _argv << ")";
419 if ( !_viewer )
421 // load plugin viewer process
422 loadViewer();
424 if ( !_viewer )
426 kDebug() << "No viewer dbus stub found";
427 return 0;
431 kDebug() << "-> ownID" << ownDBusId << " viewer ID:" << _viewerDBusId;
433 QStringList argn( _argn );
434 QStringList argv( _argv );
436 // check the mime type
437 QString mime = mimeType;
438 if (mime.isEmpty())
440 mime = lookupMimeType( url );
441 argn << "MIME";
442 argv << mime;
444 if (mime.isEmpty())
446 kDebug() << "Unknown MimeType";
447 return 0;
450 // lookup plugin for mime type
451 QString plugin_name = lookup(mime);
452 if (plugin_name.isEmpty())
454 kDebug() << "No suitable plugin";
455 return 0;
458 // get plugin class object
459 QDBusObjectPath cls_ref = _viewer->newClass( plugin_name, ownDBusId );
460 if ( cls_ref.path().isEmpty() )
462 kDebug() << "Couldn't create plugin class";
463 return 0;
466 org::kde::nsplugins::Class* cls = new org::kde::nsplugins::Class( _viewerDBusId, cls_ref.path(), QDBusConnection::sessionBus() );
468 // handle special plugin cases
469 if ( mime=="application/x-shockwave-flash" )
470 embed = true; // flash doesn't work in full mode :(
473 // get plugin instance
474 QDBusObjectPath inst_ref = cls->newInstance( url, mime, embed, argn, argv, ownDBusId, callbackId, reload );
475 if ( inst_ref.path().isEmpty() )
477 kDebug() << "Couldn't create plugin instance";
478 delete cls;
479 return 0;
482 KUrl baseUrl;
483 const int indexOfBaseurl = argn.indexOf( "__KHTML__PLUGINBASEURL" );
484 if ( indexOfBaseurl > 0 ) {
485 baseUrl = argv.at( indexOfBaseurl );
488 NSPluginInstance *plugin = new NSPluginInstance( parent, _viewerDBusId, inst_ref.path(), baseUrl);
490 kDebug() << "<- NSPluginLoader::NewInstance = " << (void*)plugin;
492 delete cls;
493 return plugin;
496 // vim: ts=4 sw=4 et
497 // kate: indent-width 4; replace-tabs on; tab-width 4; space-indent on;