Fix crash if key bindings specified in profile cannot be found. Improve
[personal-kdebase.git] / apps / nsplugins / viewer / nsplugin.cpp
blob654acc591d033cd87a17eac2a48ba4a8de28f3db
1 /*
2 This is an encapsulation of the Netscape plugin API.
4 Copyright (c) 2000 Matthias Hoelzer-Kluepfel <hoelzer@kde.org>
5 Stefan Schimanski <1Stein@gmx.de>
6 2003-2005 George Staikos <staikos@kde.org>
7 2007, 2008 Maksim Orlovich <maksim@kde.org>
8 2006, 2007, 2008 Apple Inc.
9 2008 Collabora, Ltd.
10 2008 Sebastian Sauer <mail@dipe.org>
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 2 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27 #include "nsplugin.h"
28 #include "pluginhost_xembed.h"
29 #include "pluginhost_xt.h"
30 #include "resolve.h"
31 #include "classadaptor.h"
32 #include "instanceadaptor.h"
33 #include "vieweradaptor.h"
35 #include "nsplugins_callback_interface.h"
37 #include <stdlib.h>
38 #include <unistd.h>
40 #include <QDir>
41 #include <QFile>
42 #include <QTimer>
43 #include <QApplication>
45 #ifdef Bool
46 #undef Bool
47 #endif
49 #include <kconfig.h>
50 #include <kconfiggroup.h>
51 #include <kdebug.h>
52 #include <kglobal.h>
53 #include <kio/netaccess.h>
54 #include <kprotocolmanager.h>
55 #include <klibloader.h>
56 #include <klocale.h>
57 #include <kstandarddirs.h>
58 #include <ktemporaryfile.h>
59 #include <kurl.h>
60 #include <QX11Info>
61 #include <QProcess>
63 #include <X11/Intrinsic.h>
64 #include <X11/Composite.h>
65 #include <X11/Constraint.h>
66 #include <X11/Shell.h>
67 #include <X11/StringDefs.h>
69 // provide these symbols when compiling with gcc 3.x
71 #if defined __GNUC__ && defined __GNUC_MINOR__
72 # define KDE_GNUC_PREREQ(maj, min) \
73 ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
74 #else
75 # define KDE_GNUC_PREREQ(maj, min) 0
76 #endif
79 #if defined(__GNUC__) && KDE_GNUC_PREREQ(3,0)
80 extern "C" void* __builtin_new(size_t s)
82 return operator new(s);
85 extern "C" void __builtin_delete(void* p)
87 operator delete(p);
90 extern "C" void* __builtin_vec_new(size_t s)
92 return operator new[](s);
95 extern "C" void __builtin_vec_delete(void* p)
97 operator delete[](p);
100 extern "C" void __pure_virtual()
102 abort();
104 #endif
106 // The NSPluginInstance is always the ndata of the instance. Sometimes, plug-ins will call an instance-specific function
107 // with a NULL instance. To workaround this, we remember the last NSPluginInstance produced with the
108 // NSPluginClass::newInstance() method. This specifically works around Flash and Shockwave which do e.g. call NPN_Useragent
109 // with a NULL instance When we call NPP_New.
110 // At the moment we do setLastPluginInstance() only if the NSPluginInstance is created. Probably it would be more logical
111 // to do that more often to prevent some wired situations where we may end with the wrong NSPluginInstance for a plugin.
112 NSPluginInstance* NSPluginInstance::s_lastPluginInstance = 0;
113 NSPluginInstance* NSPluginInstance::lastPluginInstance() { return s_lastPluginInstance; }
114 void NSPluginInstance::setLastPluginInstance(NSPluginInstance* inst) { s_lastPluginInstance = inst; }
115 static NSPluginInstance* pluginViewForInstance(NPP instance)
117 if (instance && instance->ndata)
118 return static_cast<NSPluginInstance*>(instance->ndata);
119 return NSPluginInstance::lastPluginInstance();
122 // server side functions -----------------------------------------------------
124 // allocate memory
125 void *g_NPN_MemAlloc(uint32 size)
127 void *mem = ::malloc(size);
129 //kDebug(1431) << "g_NPN_MemAlloc(), size=" << size << " allocated at " << mem;
131 return mem;
135 // free memory
136 void g_NPN_MemFree(void *ptr)
138 //kDebug(1431) << "g_NPN_MemFree() at " << ptr;
139 if (ptr)
140 ::free(ptr);
143 uint32 g_NPN_MemFlush(uint32 size)
145 Q_UNUSED(size);
146 //kDebug(1431) << "g_NPN_MemFlush()";
147 // MAC OS only.. we don't use this
148 return 0;
152 // redraw
153 void g_NPN_ForceRedraw(NPP /*instance*/)
155 // http://devedge.netscape.com/library/manuals/2002/plugin/1.0/npn_api3.html#999401
156 // FIXME
157 kDebug(1431) << "g_NPN_ForceRedraw() [unimplemented]";
161 // invalidate rect
162 void g_NPN_InvalidateRect(NPP /*instance*/, NPRect* /*invalidRect*/)
164 // http://devedge.netscape.com/library/manuals/2002/plugin/1.0/npn_api7.html#999503
165 // FIXME
166 kDebug(1431) << "g_NPN_InvalidateRect() [unimplemented]";
170 // invalidate region
171 void g_NPN_InvalidateRegion(NPP /*instance*/, NPRegion /*invalidRegion*/)
173 // http://devedge.netscape.com/library/manuals/2002/plugin/1.0/npn_api8.html#999528
174 // FIXME
175 kDebug(1431) << "g_NPN_InvalidateRegion() [unimplemented]";
179 // get value
180 NPError g_NPN_GetValue(NPP /*instance*/, NPNVariable variable, void *value)
182 kDebug(1431) << "g_NPN_GetValue(), variable=" << static_cast<int>(variable);
184 switch (variable)
186 case NPNVxDisplay:
187 *(void**)value = QX11Info::display();
188 return NPERR_NO_ERROR;
189 case NPNVxtAppContext:
190 *(void**)value = XtDisplayToApplicationContext(QX11Info::display());
191 return NPERR_NO_ERROR;
192 case NPNVjavascriptEnabledBool:
193 *(bool*)value = true;
194 return NPERR_NO_ERROR;
195 case NPNVasdEnabledBool:
196 // SmartUpdate - we don't do this
197 *(bool*)value = false;
198 return NPERR_NO_ERROR;
199 case NPNVisOfflineBool:
200 // Offline browsing - no thanks
201 *(bool*)value = false;
202 return NPERR_NO_ERROR;
203 case NPNVSupportsXEmbedBool:
204 // ### may depend on event loop setting
205 *(bool*)value = true;
206 return NPERR_NO_ERROR;
207 case NPNVToolkit:
208 // This is messy. OSS things want to see "Gtk2" here;
209 // but commercial flash works better if we return something else.
210 // So we return a KHTML classic, since we can work with
211 // the community members far easier.
212 *(NPNToolkitType*)value = (NPNToolkitType)0xFEEDABEE;
213 return NPERR_NO_ERROR;
214 case NPPVpluginKeepLibraryInMemory:
215 *(bool*)value = true;
216 return NPERR_NO_ERROR;
217 default:
218 kDebug(1431) << "g_NPN_GetValue(), [unimplemented] variable=" << variable;
219 return NPERR_INVALID_PARAM;
224 NPError g_NPN_DestroyStream(NPP instance, NPStream* stream,
225 NPReason reason)
227 // FIXME: is this correct? I imagine it is not. (GS)
228 kDebug(1431) << "g_NPN_DestroyStream()";
230 NSPluginInstance *inst = pluginViewForInstance(instance);
231 inst->streamFinished( (NSPluginStream *)stream->ndata );
233 switch (reason) {
234 case NPRES_DONE:
235 return NPERR_NO_ERROR;
236 case NPRES_USER_BREAK:
237 // FIXME: notify the user
238 case NPRES_NETWORK_ERR:
239 // FIXME: notify the user
240 default:
241 return NPERR_GENERIC_ERROR;
246 NPError g_NPN_RequestRead(NPStream* /*stream*/, NPByteRange* /*rangeList*/)
248 // http://devedge.netscape.com/library/manuals/2002/plugin/1.0/npn_api16.html#999734
249 kDebug(1431) << "g_NPN_RequestRead() [unimplemented]";
251 // FIXME
252 return NPERR_GENERIC_ERROR;
255 NPError g_NPN_NewStream(NPP /*instance*/, NPMIMEType /*type*/,
256 const char* /*target*/, NPStream** /*stream*/)
258 // http://devedge.netscape.com/library/manuals/2002/plugin/1.0/npn_api12.html#999628
259 kDebug(1431) << "g_NPN_NewStream() [unimplemented]";
261 // FIXME
262 // This creates a stream from the plugin to the browser of type "type" to
263 // display in "target"
264 return NPERR_GENERIC_ERROR;
267 int32 g_NPN_Write(NPP /*instance*/, NPStream* /*stream*/, int32 /*len*/, void* /*buf*/)
269 // http://devedge.netscape.com/library/manuals/2002/plugin/1.0/npn_api21.html#999859
270 kDebug(1431) << "g_NPN_Write() [unimplemented]";
272 // FIXME
273 return 0;
277 // URL functions
278 NPError g_NPN_GetURL(NPP instance, const char *url, const char *target)
280 kDebug(1431) << "g_NPN_GetURL: url=" << url << " target=" << target;
282 NSPluginInstance *inst = pluginViewForInstance(instance);
283 if (inst) {
284 inst->requestURL( QString::fromLatin1(url), QString(),
285 QString::fromLatin1(target), 0 );
288 return NPERR_NO_ERROR;
292 NPError g_NPN_GetURLNotify(NPP instance, const char *url, const char *target,
293 void* notifyData)
295 kDebug(1431) << "g_NPN_GetURLNotify: url=" << url << " target=" << target << " inst=" << (void*)instance;
296 NSPluginInstance *inst = pluginViewForInstance(instance);
297 if (inst) {
298 kDebug(1431) << "g_NPN_GetURLNotify: ndata=" << (void*)inst;
299 inst->requestURL( QString::fromLatin1(url), QString(),
300 QString::fromLatin1(target), notifyData, true );
303 return NPERR_NO_ERROR;
307 NPError g_NPN_PostURLNotify(NPP instance, const char* url, const char* target,
308 uint32 len, const char* buf, NPBool file, void* notifyData)
310 // http://devedge.netscape.com/library/manuals/2002/plugin/1.0/npn_api14.html
311 kDebug(1431) << "g_NPN_PostURLNotify() [incomplete]";
312 kDebug(1431) << "url=[" << url << "] target=[" << target << "]";
313 QByteArray postdata;
314 KParts::OpenUrlArguments args;
315 KParts::BrowserArguments browserArgs;
317 if (len == 0) {
318 return NPERR_NO_DATA;
321 if (file) { // buf is a filename
322 QFile f(buf);
323 if (!f.open(QIODevice::ReadOnly)) {
324 return NPERR_FILE_NOT_FOUND;
327 // FIXME: this will not work because we need to strip the header out!
328 postdata = f.readAll();
329 f.close();
330 } else { // buf is raw data
331 // First strip out the header
332 const char *previousStart = buf;
333 uint32 l;
334 bool previousCR = true;
336 for (l = 1;; ++l) {
337 if (l == len) {
338 break;
341 if (buf[l-1] == '\n' || (previousCR && buf[l-1] == '\r')) {
342 if (previousCR) { // header is done!
343 if ((buf[l-1] == '\r' && buf[l] == '\n') ||
344 (buf[l-1] == '\n' && buf[l] == '\r'))
345 l++;
346 l++;
347 previousStart = &buf[l-1];
348 break;
351 QString thisLine = QString::fromLatin1(previousStart, &buf[l-1] - previousStart).trimmed();
353 previousStart = &buf[l];
354 previousCR = true;
356 kDebug(1431) << "Found header line: [" << thisLine << "]";
357 if (thisLine.startsWith("Content-Type: ")) {
358 browserArgs.setContentType(thisLine);
360 } else {
361 previousCR = false;
365 postdata = QByteArray(previousStart, len - l + 1);
368 kDebug(1431) << "Post data: " << postdata.size() << " bytes";
369 #if 0
370 QFile f("/tmp/nspostdata");
371 f.open(QIODevice::WriteOnly);
372 f.write(postdata);
373 f.close();
374 #endif
376 if (!target || !*target) {
377 // Send the results of the post to the plugin
378 // (works by default)
379 } else if (!strcmp(target, "_current") || !strcmp(target, "_self") ||
380 !strcmp(target, "_top")) {
381 // Unload the plugin, put the results in the frame/window that the
382 // plugin was loaded in
383 // FIXME
384 } else if (!strcmp(target, "_new") || !strcmp(target, "_blank")){
385 // Open a new browser window and write the results there
386 // FIXME
387 } else {
388 // Write the results to the specified frame
389 // FIXME
392 NSPluginInstance *inst = pluginViewForInstance(instance);
393 if (inst && !inst->normalizedURL(QString::fromLatin1(url)).isNull()) {
394 inst->postURL( QString::fromLatin1(url), postdata, browserArgs.contentType(),
395 QString::fromLatin1(target), notifyData, args, browserArgs, true );
396 } else {
397 // Unsupported / insecure
398 return NPERR_INVALID_URL;
401 return NPERR_NO_ERROR;
405 NPError g_NPN_PostURL(NPP instance, const char* url, const char* target,
406 uint32 len, const char* buf, NPBool file)
408 // http://devedge.netscape.com/library/manuals/2002/plugin/1.0/npn_api13.html
409 kDebug(1431) << "g_NPN_PostURL()";
410 kDebug(1431) << "url=[" << url << "] target=[" << target << "]";
411 QByteArray postdata;
412 KParts::OpenUrlArguments args;
413 KParts::BrowserArguments browserArgs;
415 if (len == 0) {
416 return NPERR_NO_DATA;
419 if (file) { // buf is a filename
420 QFile f(buf);
421 if (!f.open(QIODevice::ReadOnly)) {
422 return NPERR_FILE_NOT_FOUND;
425 // FIXME: this will not work because we need to strip the header out!
426 postdata = f.readAll();
427 f.close();
428 } else { // buf is raw data
429 // First strip out the header
430 const char *previousStart = buf;
431 uint32 l;
432 bool previousCR = true;
434 for (l = 1;; ++l) {
435 if (l == len) {
436 break;
439 if (buf[l-1] == '\n' || (previousCR && buf[l-1] == '\r')) {
440 if (previousCR) { // header is done!
441 if ((buf[l-1] == '\r' && buf[l] == '\n') ||
442 (buf[l-1] == '\n' && buf[l] == '\r'))
443 l++;
444 l++;
445 previousStart = &buf[l-1];
446 break;
449 QString thisLine = QString::fromLatin1(previousStart, &buf[l-1] - previousStart).trimmed();
451 previousStart = &buf[l];
452 previousCR = true;
454 kDebug(1431) << "Found header line: [" << thisLine << "]";
455 if (thisLine.startsWith("Content-Type: ")) {
456 browserArgs.setContentType(thisLine);
458 } else {
459 previousCR = false;
463 postdata = QByteArray(previousStart, len - l + 1);
466 kDebug(1431) << "Post data: " << postdata.size() << " bytes";
467 #if 0
468 QFile f("/tmp/nspostdata");
469 f.open(QIODevice::WriteOnly);
470 f.write(postdata);
471 f.close();
472 #endif
474 if (!target || !*target) {
475 // Send the results of the post to the plugin
476 // (works by default)
477 } else if (!strcmp(target, "_current") || !strcmp(target, "_self") ||
478 !strcmp(target, "_top")) {
479 // Unload the plugin, put the results in the frame/window that the
480 // plugin was loaded in
481 // FIXME
482 } else if (!strcmp(target, "_new") || !strcmp(target, "_blank")){
483 // Open a new browser window and write the results there
484 // FIXME
485 } else {
486 // Write the results to the specified frame
487 // FIXME
490 NSPluginInstance *inst = pluginViewForInstance(instance);
491 if (inst && !inst->normalizedURL(QString::fromLatin1(url)).isNull()) {
492 inst->postURL( QString::fromLatin1(url), postdata, browserArgs.contentType(),
493 QString::fromLatin1(target), 0L, args, browserArgs, false );
494 } else {
495 // Unsupported / insecure
496 return NPERR_INVALID_URL;
499 return NPERR_NO_ERROR;
503 // display status message
504 void g_NPN_Status(NPP instance, const char *message)
506 kDebug(1431) << "g_NPN_Status(): " << message;
508 if (!instance)
509 return;
511 // turn into an instance signal
512 NSPluginInstance *inst = pluginViewForInstance(instance);
514 inst->emitStatus(message);
518 static QByteArray uaStore;
519 static QByteArray uaEmpty("Gecko");
521 // inquire user agent
522 const char *g_NPN_UserAgent(NPP instance)
524 if (!instance)
525 return uaEmpty.data();
527 if (uaStore.isEmpty()) {
528 KProtocolManager kpm;
529 QString agent = kpm.userAgentForHost("nspluginviewer");
530 uaStore = agent.toLatin1();
533 kDebug(1431) << "g_NPN_UserAgent() = " << uaStore;
534 return uaStore.data();
538 // inquire version information
539 void g_NPN_Version(int *plugin_major, int *plugin_minor, int *browser_major, int *browser_minor)
541 kDebug(1431) << "g_NPN_Version()";
543 // FIXME: Use the sensible values
544 *browser_major = NP_VERSION_MAJOR;
545 *browser_minor = NP_VERSION_MINOR;
547 *plugin_major = NP_VERSION_MAJOR;
548 *plugin_minor = NP_VERSION_MINOR;
552 void g_NPN_ReloadPlugins(NPBool reloadPages)
554 // http://devedge.netscape.com/library/manuals/2002/plugin/1.0/npn_api15.html#999713
555 kDebug(1431) << "g_NPN_ReloadPlugins()";
556 QString prog = KGlobal::dirs()->findExe("nspluginscan");
558 if (reloadPages) {
559 // This is the proper way, but it cannot be done because we have no
560 // handle to the caller! How stupid! We cannot force all konqi windows
561 // to reload - that would be evil.
562 //p.start(K3Process::Block);
563 // Let's only allow the caller to be reloaded, not everything.
564 //if (_callback)
565 // _callback->reloadPage();
566 QProcess::startDetached(prog);
567 } else {
568 QProcess::startDetached(prog);
573 // JAVA functions
574 JRIEnv *g_NPN_GetJavaEnv()
576 kDebug(1431) << "g_NPN_GetJavaEnv() [unimplemented]";
577 // FIXME - what do these do? I can't find docs, and even Mozilla doesn't
578 // implement them
579 return 0;
583 jref g_NPN_GetJavaPeer(NPP /*instance*/)
585 kDebug(1431) << "g_NPN_GetJavaPeer() [unimplemented]";
586 // FIXME - what do these do? I can't find docs, and even Mozilla doesn't
587 // implement them
588 return 0;
592 NPError g_NPN_SetValue(NPP /*instance*/, NPPVariable variable, void* /*value*/)
594 kDebug(1431) << "g_NPN_SetValue() [unimplemented]";
595 switch (variable) {
596 case NPPVpluginWindowBool:
597 // FIXME
598 // If true, the plugin is windowless. If false, it is in a window.
599 case NPPVpluginTransparentBool:
600 // FIXME
601 // If true, the plugin is displayed transparent
602 default:
603 return NPERR_GENERIC_ERROR;
610 These two are in the ABI version 16 which we don't claim to support, but
611 flash uses anyway.
613 static void g_NPN_PushPopupsEnabledState(NPP /*instance*/, NPBool enabled)
615 kDebug(1431) << "[unimplemented]" << enabled;
618 static void g_NPN_PopPopupsEnabledState(NPP /*instance*/)
620 kDebug(1431) << "[unimplemented]";
623 /******************************************************************/
625 static int s_instanceCounter = 0;
627 NSPluginInstance::NSPluginInstance(NPP privateData, NPPluginFuncs *pluginFuncs,
628 KLibrary *handle,
629 const QString &src, const QString &/*mime*/,
630 const QString &appId, const QString &callbackId,
631 bool embed,
632 QObject *parent )
633 : QObject( parent )
635 // The object name is the dbus object path
636 (void) new InstanceAdaptor( this );
637 setObjectName( QString( "/Instance_" ) + QString::number( ++s_instanceCounter ) );
638 QDBusConnection::sessionBus().registerObject( objectName(), this );
640 Q_UNUSED(embed);
641 _embedded = false;
642 _npp = privateData;
643 _npp->ndata = this;
644 _destroyed = false;
645 _handle = handle;
646 _callback = new org::kde::nsplugins::CallBack( appId, callbackId, QDBusConnection::sessionBus() );
648 KUrl base(src);
649 base.setFileName( QString() );
650 _baseURL = base.url();
652 memcpy(&_pluginFuncs, pluginFuncs, sizeof(_pluginFuncs));
654 _timer = new QTimer( this );
655 connect( _timer, SIGNAL(timeout()), SLOT(timer()) );
657 kDebug(1431) << "NSPluginInstance::NSPluginInstance";
658 kDebug(1431) << "pdata = " << _npp->pdata;
659 kDebug(1431) << "ndata = " << _npp->ndata;
661 // Create the appropriate host for the plugin type.
662 _pluginHost = 0;
663 int result = PR_FALSE;
665 //### iceweasel does something odd here --- it enabled XEmbed for swfdec,
666 // even though that doesn't provide GetValue at all(!)
667 if (NPGetValue(NPPVpluginNeedsXEmbed, &result) == NPERR_NO_ERROR && result) {
668 kDebug(1431) << "plugin reqests XEmbed";
669 _pluginHost = new PluginHostXEmbed(this);
670 } else {
671 kDebug(1431) << "plugin requests Xt";
672 _pluginHost = new PluginHostXt(this);
675 XSync(QX11Info::display(), false);
678 NSPluginInstance::~NSPluginInstance()
680 kDebug(1431) << "-> ~NSPluginInstance";
681 destroy();
682 kDebug(1431) << "<- ~NSPluginInstance";
685 void NSPluginInstance::destroy()
687 if ( !_destroyed ) {
689 kDebug(1431) << "delete streams";
690 qDeleteAll( _waitingRequests );
692 while ( !_streams.isEmpty() ) {
693 NSPluginStreamBase *s = _streams.takeFirst();
694 s->stop();
695 delete s;
698 kDebug(1431) << "delete tempfiles";
699 qDeleteAll( _tempFiles );
701 kDebug(1431) << "delete callbacks";
702 delete _callback;
703 _callback = 0;
705 kDebug(1431) << "destroy plugin";
706 NPSavedData *saved = 0;
708 // As of 7/31/01, nsplugin crashes when used with Qt
709 // linked with libGL if the destroy function is called.
710 // A patch on that date hacked out the following call.
711 // On 11/17/01, Jeremy White has reenabled this destroy
712 // in a an attempt to better understand why this crash
713 // occurs so that the real problem can be found and solved.
714 // It's possible that a flaw in the SetWindow call
715 // caused the crash and it is now fixed.
716 if ( _pluginFuncs.destroy )
717 _pluginFuncs.destroy( _npp, &saved );
719 if (saved && saved->len && saved->buf)
720 g_NPN_MemFree(saved->buf);
721 if (saved)
722 g_NPN_MemFree(saved);
724 delete _pluginHost;
725 _pluginHost = 0;
727 if (_npp) {
728 ::free(_npp); // matched with malloc() in newInstance
731 _destroyed = true;
736 void NSPluginInstance::shutdown()
738 NSPluginClass *cls = dynamic_cast<NSPluginClass*>(parent());
739 //destroy();
740 if (cls) {
741 cls->destroyInstance( this );
746 void NSPluginInstance::timer()
748 if (!_embedded) {
749 _timer->setSingleShot( true );
750 _timer->start( 100 );
751 return;
754 //_streams.clear();
756 // start queued requests
757 kDebug(1431) << "looking for waiting requests";
758 while ( !_waitingRequests.isEmpty() ) {
759 kDebug(1431) << "request found";
760 Request req( *_waitingRequests.head() );
761 delete _waitingRequests.dequeue();
763 QString url;
765 // make absolute url
766 if ( req.url.left(11).toLower()=="javascript:" )
767 url = req.url;
768 else if ( KUrl::isRelativeUrl(req.url) ) {
769 KUrl bu( _baseURL );
770 KUrl absUrl( bu, req.url );
771 url = absUrl.url();
772 } else if ( req.url[0]=='/' && KUrl(_baseURL).hasHost() ) {
773 KUrl absUrl( _baseURL );
774 absUrl.setPath( req.url );
775 url = absUrl.url();
776 } else
777 url = req.url;
779 // non empty target = frame target
780 if ( !req.target.isEmpty())
782 if (_callback)
784 if ( req.post ) {
785 _callback->postURL( url, req.target, req.data, req.mime );
786 } else {
787 _callback->requestURL( url, req.target );
789 if ( req.notify ) {
790 NPURLNotify( req.url, NPRES_DONE, req.notify );
793 } else {
794 if (!url.isEmpty())
796 kDebug(1431) << "Starting new stream " << req.url;
798 if (req.post) {
799 // create stream
800 NSPluginStream *s = new NSPluginStream( this );
801 connect( s, SIGNAL(finished(NSPluginStreamBase*)),
802 SLOT(streamFinished(NSPluginStreamBase*)) );
803 _streams.append( s );
805 kDebug() << "posting to " << url;
807 emitStatus( i18n("Submitting data to %1", url) );
808 s->post( url, req.data, req.mime, req.notify, req.args, req.browserArgs );
809 } else if (url.toLower().startsWith("javascript:")){
810 if (_callback) {
811 static int _jsrequestid = 0;
812 _jsrequests.insert(_jsrequestid, new Request(req));
813 _callback->evalJavaScript(_jsrequestid++, url.mid(11));
814 } else {
815 kDebug() << "No callback for javascript: url!";
817 } else {
818 // create stream
819 NSPluginStream *s = new NSPluginStream( this );
820 connect( s, SIGNAL(finished(NSPluginStreamBase*)),
821 SLOT(streamFinished(NSPluginStreamBase*)) );
822 _streams.append( s );
824 kDebug() << "getting " << url;
826 emitStatus( i18n("Requesting %1", url) );
827 s->get( url, req.mime, req.notify, req.reload );
830 //break;
837 QString NSPluginInstance::normalizedURL(const QString& url) const {
839 // ### for dfaure: KUrl(KUrl("http://www.youtube.com/?v=JvOSnRD5aNk"), KUrl("javascript:window.location+"__flashplugin_unique__"));
841 //### hack, prolly evil, etc.
842 if (url.startsWith("javascript:"))
843 return url;
845 KUrl bu( _baseURL );
846 KUrl inURL(bu, url);
847 KConfig _cfg( "kcmnspluginrc" );
848 KConfigGroup cfg(&_cfg, "Misc");
850 if (!cfg.readEntry("HTTP URLs Only", false) ||
851 inURL.protocol() == "http" ||
852 inURL.protocol() == "https" ||
853 inURL.protocol() == "javascript") {
854 return inURL.url();
857 // Allow: javascript:, http, https, or no protocol (match loading)
858 kDebug(1431) << "NSPluginInstance::normalizedURL - I don't think so. http or https only!";
859 return QString();
863 void NSPluginInstance::requestURL( const QString &url, const QString &mime,
864 const QString &target, void *notify, bool forceNotify, bool reload )
866 // Generally this should already be done, but let's be safe for now.
867 QString nurl = normalizedURL(url);
868 if (nurl.isNull()) {
869 return;
872 kDebug(1431) << "NSPluginInstance::requestURL url=" << nurl << " target=" << target << " notify=" << notify;
873 _waitingRequests.enqueue( new Request( nurl, mime, target, notify, forceNotify, reload ) );
874 _timer->setSingleShot( true );
875 _timer->start( 100 );
879 void NSPluginInstance::postURL( const QString &url, const QByteArray& data,
880 const QString &mime,
881 const QString &target, void *notify,
882 const KParts::OpenUrlArguments& args,
883 const KParts::BrowserArguments& browserArgs,
884 bool forceNotify )
886 // Generally this should already be done, but let's be safe for now.
887 QString nurl = normalizedURL(url);
888 if (nurl.isNull()) {
889 return;
892 kDebug(1431) << "NSPluginInstance::postURL url=" << nurl << " target=" << target << " notify=" << notify;
893 _waitingRequests.enqueue( new Request( nurl, data, mime, target, notify, args, browserArgs, forceNotify) );
894 _timer->setSingleShot( true );
895 _timer->start( 100 );
899 void NSPluginInstance::emitStatus(const QString &message)
901 if( _callback )
902 _callback->statusMessage( message );
906 void NSPluginInstance::streamFinished( NSPluginStreamBase* strm )
908 kDebug(1431) << "-> NSPluginInstance::streamFinished";
909 emitStatus( QString() );
910 _streams.removeOne(strm);
911 strm->deleteLater();
912 _timer->setSingleShot( true );
913 _timer->start( 100 );
916 void NSPluginInstance::setupWindow(int winId, int w, int h)
918 kDebug(1431) << "-> NSPluginInstance::setupWindow( winid =" << winId << " w=" << w << ", h=" << h << " ) ";
919 if (_pluginHost)
920 _pluginHost->setupWindow(winId, w, h);
921 else
922 kWarning(1431) << "No plugin host!";
924 kDebug(1431) << "<- NSPluginInstance::setupWindow";
925 _width = w;
926 _height = h;
927 _embedded = true;
930 void NSPluginInstance::resizePlugin(int clientWinId, int w, int h)
932 kDebug() << _width << w << _height << h << _embedded;
933 if (!_embedded)
934 return;
935 if (w == _width && h == _height)
936 return;
937 _pluginHost->resizePlugin(clientWinId, w, h);
938 _width = w;
939 _height = h;
943 void NSPluginInstance::javascriptResult(int id, const QString &result) {
944 QMap<int, Request*>::iterator i = _jsrequests.find( id );
945 if (i != _jsrequests.end()) {
946 Request *req = i.value();
947 _jsrequests.erase( i );
948 NSPluginStream *s = new NSPluginStream( this );
949 connect( s, SIGNAL(finished(NSPluginStreamBase*)),
950 SLOT(streamFinished(NSPluginStreamBase*)) );
951 _streams.append( s );
953 int len = result.length();
954 s->create( req->url, QString("text/plain"), req->notify, req->forceNotify );
955 kDebug(1431) << "javascriptResult has been called with: "<<result;
956 if (len > 0) {
957 QByteArray data(len + 1, 0);
958 memcpy(data.data(), result.toLatin1(), len);
959 data[len] = 0;
960 s->process(data, 0);
961 } else {
962 len = 7; // "unknown"
963 QByteArray data(len + 1, 0);
964 memcpy(data.data(), "unknown", len);
965 data[len] = 0;
966 s->process(data, 0);
968 s->finish(false);
970 delete req;
975 NPError NSPluginInstance::NPGetValue(NPPVariable variable, void *value)
977 if( value==0 ) {
978 kDebug() << "FIXME: value==0 in NSPluginInstance::NPGetValue";
979 return NPERR_GENERIC_ERROR;
982 if (!_pluginFuncs.getvalue)
983 return NPERR_GENERIC_ERROR;
985 NPError error = _pluginFuncs.getvalue(_npp, variable, value);
987 CHECK(GetValue,error);
991 NPError NSPluginInstance::NPSetValue(NPNVariable variable, void *value)
993 if( value==0 ) {
994 kDebug() << "FIXME: value==0 in NSPluginInstance::NPSetValue";
995 return NPERR_GENERIC_ERROR;
998 if (!_pluginFuncs.setvalue)
999 return NPERR_GENERIC_ERROR;
1001 NPError error = _pluginFuncs.setvalue(_npp, variable, value);
1003 CHECK(SetValue,error);
1007 NPError NSPluginInstance::NPSetWindow(NPWindow *window)
1009 if( window==0 ) {
1010 kDebug() << "FIXME: window==0 in NSPluginInstance::NPSetWindow";
1011 return NPERR_GENERIC_ERROR;
1014 if (!_pluginFuncs.setwindow)
1015 return NPERR_GENERIC_ERROR;
1017 NPError error = _pluginFuncs.setwindow(_npp, window);
1019 CHECK(SetWindow,error);
1023 NPError NSPluginInstance::NPDestroyStream(NPStream *stream, NPReason reason)
1025 if( stream==0 ) {
1026 kDebug() << "FIXME: stream==0 in NSPluginInstance::NPDestroyStream";
1027 return NPERR_GENERIC_ERROR;
1030 if (!_pluginFuncs.destroystream)
1031 return NPERR_GENERIC_ERROR;
1033 NPError error = _pluginFuncs.destroystream(_npp, stream, reason);
1035 CHECK(DestroyStream,error);
1039 NPError NSPluginInstance::NPNewStream(NPMIMEType type, NPStream *stream, NPBool seekable, uint16 *stype)
1041 if( stream==0 ) {
1042 kDebug() << "FIXME: stream==0 in NSPluginInstance::NPNewStream";
1043 return NPERR_GENERIC_ERROR;
1046 if( stype==0 ) {
1047 kDebug() << "FIXME: stype==0 in NSPluginInstance::NPNewStream";
1048 return NPERR_GENERIC_ERROR;
1051 if (!_pluginFuncs.newstream)
1052 return NPERR_GENERIC_ERROR;
1054 NPError error = _pluginFuncs.newstream(_npp, type, stream, seekable, stype);
1056 CHECK(NewStream,error);
1060 void NSPluginInstance::NPStreamAsFile(NPStream *stream, const char *fname)
1062 if( stream==0 ) {
1063 kDebug() << "FIXME: stream==0 in NSPluginInstance::NPStreamAsFile";
1064 return;
1067 if( fname==0 ) {
1068 kDebug() << "FIXME: fname==0 in NSPluginInstance::NPStreamAsFile";
1069 return;
1072 if (!_pluginFuncs.asfile)
1073 return;
1075 _pluginFuncs.asfile(_npp, stream, fname);
1079 int32 NSPluginInstance::NPWrite(NPStream *stream, int32 offset, int32 len, void *buf)
1081 if( stream==0 ) {
1082 kDebug() << "FIXME: stream==0 in NSPluginInstance::NPWrite";
1083 return 0;
1086 if( buf==0 ) {
1087 kDebug() << "FIXME: buf==0 in NSPluginInstance::NPWrite";
1088 return 0;
1091 if (!_pluginFuncs.write)
1092 return 0;
1094 return _pluginFuncs.write(_npp, stream, offset, len, buf);
1098 int32 NSPluginInstance::NPWriteReady(NPStream *stream)
1100 if( stream==0 ) {
1101 kDebug() << "FIXME: stream==0 in NSPluginInstance::NPWriteReady";
1102 return 0;
1105 if (!_pluginFuncs.writeready)
1106 return 0;
1108 return _pluginFuncs.writeready(_npp, stream);
1112 void NSPluginInstance::NPURLNotify(const QString &url, NPReason reason, void *notifyData)
1114 if (!_pluginFuncs.urlnotify)
1115 return;
1117 _pluginFuncs.urlnotify(_npp, url.toAscii(), reason, notifyData);
1121 void NSPluginInstance::addTempFile(KTemporaryFile *tmpFile)
1123 _tempFiles.append(tmpFile);
1126 static bool has_focus = false;
1128 void NSPluginInstance::gotFocusIn()
1130 has_focus = true;
1133 void NSPluginInstance::gotFocusOut()
1135 has_focus = false;
1138 #include <dlfcn.h>
1139 // Prevent plugins from polling the keyboard regardless of focus.
1140 static int (*real_xquerykeymap)( Display*, char[32] ) = NULL;
1142 extern "C" KDE_EXPORT
1143 int XQueryKeymap( Display* dpy, char k[32] )
1145 if( real_xquerykeymap == NULL )
1146 real_xquerykeymap = (int (*)( Display*, char[32] )) dlsym( RTLD_NEXT, "XQueryKeymap" );
1147 if( has_focus )
1148 return real_xquerykeymap( dpy, k );
1149 memset( k, 0, 32 );
1150 return 1;
1154 /***************************************************************************/
1156 NSPluginViewer::NSPluginViewer( QObject *parent )
1157 : QObject( parent )
1159 (void) new ViewerAdaptor( this );
1160 QDBusConnection::sessionBus().registerObject( "/Viewer", this );
1162 QObject::connect(QDBusConnection::sessionBus().interface(),
1163 SIGNAL(serviceOwnerChanged(const QString&, const QString&, const QString&)),
1164 this, SLOT(appChanged( const QString&, const QString&, const QString&)));
1168 NSPluginViewer::~NSPluginViewer()
1170 kDebug(1431) << "NSPluginViewer::~NSPluginViewer";
1174 void NSPluginViewer::appChanged( const QString& id, const QString& oldOwner, const QString& newOwner) {
1175 if ( oldOwner.isEmpty() || !newOwner.isEmpty() ) // only care about unregistering apps
1176 return;
1178 QMap<QString, NSPluginClass*>::iterator it = _classes.begin();
1179 const QMap<QString, NSPluginClass*>::iterator end = _classes.end();
1180 for ( ; it != end; ++it )
1182 if (it.value()->app() == oldOwner) {
1183 it = _classes.erase(it);
1187 if (_classes.isEmpty()) {
1188 shutdown();
1193 void NSPluginViewer::shutdown()
1195 kDebug(1431) << "NSPluginViewer::shutdown";
1196 _classes.clear();
1197 qApp->quit();
1201 QDBusObjectPath NSPluginViewer::newClass( const QString& plugin, const QString& senderId )
1203 kDebug(1431) << "NSPluginViewer::NewClass( " << plugin << ")";
1205 // search existing class
1206 NSPluginClass *cls = _classes.value( plugin );
1207 if ( !cls ) {
1208 // create new class
1209 cls = new NSPluginClass( plugin, this );
1210 cls->setApp(senderId.toLatin1());
1211 if ( cls->error() ) {
1212 kError(1431) << "Can't create plugin class" << endl;
1213 delete cls;
1214 return QDBusObjectPath();
1217 _classes.insert( plugin, cls );
1220 return QDBusObjectPath(cls->objectName());
1224 /****************************************************************************/
1226 static int s_classCounter = 0;
1228 bool NSPluginClass::s_initedGTK = false;
1230 typedef void gtkInitFunc(int *argc, char ***argv);
1232 NSPluginClass::NSPluginClass( const QString &library,
1233 QObject *parent )
1234 : QObject( parent )
1236 (void) new ClassAdaptor( this );
1237 // The object name is used to store the dbus object path
1238 setObjectName( QString( "/Class_" ) + QString::number( ++s_classCounter ) );
1239 QDBusConnection::sessionBus().registerObject( objectName(), this );
1241 // initialize members
1242 _handle = KLibLoader::self()->library(QFile::encodeName(library));
1243 _libname = library;
1244 _constructed = false;
1245 _error = true;
1246 _NP_GetMIMEDescription = 0;
1247 _NP_Initialize = 0;
1248 _NP_Shutdown = 0;
1250 _timer = new QTimer( this );
1251 connect( _timer, SIGNAL(timeout()), SLOT(timer()) );
1253 // check lib handle
1254 if (!_handle) {
1255 kDebug(1431) << "Could not dlopen " << library;
1256 return;
1259 // get exported lib functions
1260 _NP_GetMIMEDescription = (NP_GetMIMEDescriptionUPP *)_handle->resolveFunction("NP_GetMIMEDescription");
1261 _NP_Initialize = (NP_InitializeUPP *)_handle->resolveFunction("NP_Initialize");
1262 _NP_Shutdown = (NP_ShutdownUPP *)_handle->resolveFunction("NP_Shutdown");
1264 // check for valid returned ptrs
1265 if (!_NP_GetMIMEDescription) {
1266 kDebug(1431) << "Could not get symbol NP_GetMIMEDescription";
1267 return;
1270 if (!_NP_Initialize) {
1271 kDebug(1431) << "Could not get symbol NP_Initialize";
1272 return;
1275 if (!_NP_Shutdown) {
1276 kDebug(1431) << "Could not get symbol NP_Shutdown";
1277 return;
1280 // initialize plugin
1281 kDebug(1431) << "Plugin library " << library << " loaded!";
1283 // see if it uses gtk
1284 if (!s_initedGTK) {
1285 gtkInitFunc* gtkInit = (gtkInitFunc*)_handle->resolveFunction("gtk_init");
1286 if (gtkInit) {
1287 kDebug(1431) << "Calling gtk_init for the plugin";
1288 // Prevent gtk_init() from replacing the X error handlers, since the Gtk
1289 // handlers abort when they receive an X error, thus killing the viewer.
1290 int (*old_error_handler)(Display*,XErrorEvent*) = XSetErrorHandler(0);
1291 int (*old_io_error_handler)(Display*) = XSetIOErrorHandler(0);
1292 gtkInit(0, 0);
1293 XSetErrorHandler(old_error_handler);
1294 XSetIOErrorHandler(old_io_error_handler);
1295 s_initedGTK = true;
1299 _constructed = true;
1300 _error = initialize()!=NPERR_NO_ERROR;
1304 NSPluginClass::~NSPluginClass()
1306 qDeleteAll( _instances );
1307 qDeleteAll( _trash );
1309 shutdown();
1310 if (_handle)
1311 _handle->unload();
1315 void NSPluginClass::timer()
1317 // delete instances
1318 while ( !_trash.isEmpty() ) {
1319 NSPluginInstance *it = _trash.takeFirst();
1320 int i = _instances.indexOf(it);
1321 if ( i != -1 )
1322 delete _instances.takeAt(i);
1323 else // there should be no instansces in trash, which are not in _instances
1324 delete it;
1329 int NSPluginClass::initialize()
1331 kDebug(1431) << "NSPluginClass::Initialize()";
1333 if ( !_constructed )
1334 return NPERR_GENERIC_ERROR;
1336 // initialize nescape exported functions
1337 memset(&_pluginFuncs, 0, sizeof(_pluginFuncs));
1338 memset(&_nsFuncs, 0, sizeof(_nsFuncs));
1340 _pluginFuncs.size = sizeof(_pluginFuncs);
1341 _nsFuncs.size = sizeof(_nsFuncs);
1342 _nsFuncs.version = (NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR;
1343 _nsFuncs.geturl = g_NPN_GetURL;
1344 _nsFuncs.posturl = g_NPN_PostURL;
1345 _nsFuncs.requestread = g_NPN_RequestRead;
1346 _nsFuncs.newstream = g_NPN_NewStream;
1347 _nsFuncs.write = g_NPN_Write;
1348 _nsFuncs.destroystream = g_NPN_DestroyStream;
1349 _nsFuncs.status = g_NPN_Status;
1350 _nsFuncs.uagent = g_NPN_UserAgent;
1351 _nsFuncs.memalloc = g_NPN_MemAlloc;
1352 _nsFuncs.memfree = g_NPN_MemFree;
1353 _nsFuncs.memflush = g_NPN_MemFlush;
1354 _nsFuncs.reloadplugins = g_NPN_ReloadPlugins;
1355 _nsFuncs.getJavaEnv = g_NPN_GetJavaEnv;
1356 _nsFuncs.getJavaPeer = g_NPN_GetJavaPeer;
1357 _nsFuncs.geturlnotify = g_NPN_GetURLNotify;
1358 _nsFuncs.posturlnotify = g_NPN_PostURLNotify;
1359 _nsFuncs.getvalue = g_NPN_GetValue;
1360 _nsFuncs.setvalue = g_NPN_SetValue;
1361 _nsFuncs.invalidaterect = g_NPN_InvalidateRect;
1362 _nsFuncs.invalidateregion = g_NPN_InvalidateRegion;
1363 _nsFuncs.forceredraw = g_NPN_ForceRedraw;
1365 _nsFuncs.pushpopupsenabledstate = g_NPN_PushPopupsEnabledState;
1366 _nsFuncs.poppopupsenabledstate = g_NPN_PopPopupsEnabledState;
1368 // initialize plugin
1369 NPError error = _NP_Initialize(&_nsFuncs, &_pluginFuncs);
1370 assert(_nsFuncs.size == sizeof(_nsFuncs));
1371 CHECK(Initialize,error);
1375 QString NSPluginClass::getMIMEDescription()
1377 return _NP_GetMIMEDescription();
1381 void NSPluginClass::shutdown()
1383 kDebug(1431) << "NSPluginClass::shutdown error=" << _error;
1384 if( _NP_Shutdown && !_error )
1385 _NP_Shutdown();
1389 QDBusObjectPath NSPluginClass::newInstance( const QString &url, const QString &mimeType, bool embed,
1390 const QStringList &argn, const QStringList &argv,
1391 const QString &appId, const QString &callbackId, bool reload )
1393 kDebug(1431) << "-> NSPluginClass::NewInstance";
1395 if ( !_constructed )
1396 return QDBusObjectPath();
1398 // copy parameters over
1399 unsigned int argc = argn.count();
1400 char **_argn = new char*[argc];
1401 char **_argv = new char*[argc];
1402 QString src = url;
1403 QString baseURL = url;
1405 for (unsigned int i=0; i<argc; i++)
1407 QByteArray encN = argn[i].toUtf8();
1408 QByteArray encV = argv[i].toUtf8();
1410 const char *n = encN;
1411 const char *v = encV;
1413 _argn[i] = strdup(n);
1414 _argv[i] = strdup(v);
1416 if (!strcasecmp(_argn[i], "__KHTML__PLUGINBASEURL")) baseURL = _argv[i];
1417 kDebug(1431) << "argn=" << _argn[i] << " argv=" << _argv[i];
1420 // create plugin instance
1421 char mime[256];
1422 strncpy(mime, mimeType.toAscii(), 255);
1423 mime[255] = 0;
1424 NPP npp = (NPP)malloc(sizeof(NPP_t)); // I think we should be using
1425 // malloc here, just to be safe,
1426 // since the nsplugin plays with
1427 // this thing
1428 memset(npp, 0, sizeof(NPP_t));
1429 npp->ndata = NULL;
1431 // create plugin instance
1432 NPError error = _pluginFuncs.newp(mime, npp, embed ? NP_EMBED : NP_FULL,
1433 argc, _argn, _argv, 0);
1435 kDebug(1431) << "NPP_New = " << (int)error;
1437 // free arrays with arguments
1438 delete [] _argn;
1439 delete [] _argv;
1441 // check for error
1442 if ( error!=NPERR_NO_ERROR)
1444 ::free(npp);
1445 kDebug(1431) << "<- PluginClass::NewInstance = 0";
1446 return QDBusObjectPath();
1449 // Create plugin instance object
1450 NSPluginInstance *inst = new NSPluginInstance( npp, &_pluginFuncs, _handle,
1451 baseURL, mimeType, appId,
1452 callbackId, embed, this );
1454 // set the current plugin instance
1455 NSPluginInstance::setLastPluginInstance(inst);
1457 // create source stream
1458 if ( !src.isEmpty() )
1459 inst->requestURL( src, mimeType, QString(), 0, false, reload );
1461 _instances.append( inst );
1462 return QDBusObjectPath(inst->objectName());
1466 void NSPluginClass::destroyInstance( NSPluginInstance* inst )
1468 // be sure we don't deal with a dangling pointer
1469 if ( NSPluginInstance::lastPluginInstance() == inst )
1470 NSPluginInstance::setLastPluginInstance(0);
1472 // mark for destruction
1473 _trash.append( inst );
1474 timer(); //_timer->start( 0, TRUE );
1478 /****************************************************************************/
1480 NSPluginStreamBase::NSPluginStreamBase( NSPluginInstance *instance )
1481 : QObject( instance ), _instance(instance), _stream(0), _tempFile(0L),
1482 _pos(0), _queuePos(0), _error(false)
1484 _informed = false;
1488 NSPluginStreamBase::~NSPluginStreamBase()
1490 if (_stream) {
1491 _instance->NPDestroyStream( _stream, NPRES_USER_BREAK );
1492 if (_stream && _stream->url)
1493 free(const_cast<char*>(_stream->url));
1494 delete _stream;
1495 _stream = 0;
1498 delete _tempFile;
1499 _tempFile = 0;
1503 void NSPluginStreamBase::stop()
1505 finish( true );
1508 void NSPluginStreamBase::inform()
1511 if (! _informed)
1513 KUrl src(_url);
1515 _informed = true;
1517 // inform the plugin
1518 _instance->NPNewStream( _mimeType.isEmpty() ? (char *) "text/plain" : (char*)_mimeType.data(),
1519 _stream, false, &_streamType );
1520 kDebug(1431) << "NewStream stype=" << _streamType << " url=" << _url << " mime=" << _mimeType;
1522 // prepare data transfer
1523 _tempFile = 0L;
1525 if ( _streamType==NP_ASFILE || _streamType==NP_ASFILEONLY ) {
1526 _onlyAsFile = _streamType==NP_ASFILEONLY;
1527 if ( KUrl(_url).isLocalFile() ) {
1528 kDebug(1431) << "local file";
1529 // local file can be passed directly
1530 _fileURL = KUrl(_url).path();
1532 // without streaming stream is finished already
1533 if ( _onlyAsFile ) {
1534 kDebug() << "local file AS_FILE_ONLY";
1535 finish( false );
1537 } else {
1538 kDebug() << "remote file";
1540 // stream into temporary file (use lower() in case the
1541 // filename as an upper case X in it)
1542 _tempFile = new KTemporaryFile;
1543 _tempFile->open();
1544 _fileURL = _tempFile->fileName();
1545 kDebug() << "saving into " << _fileURL;
1552 bool NSPluginStreamBase::create( const QString& url, const QString& mimeType, void *notify, bool forceNotify)
1554 if ( _stream )
1555 return false;
1557 _url = url;
1558 _notifyData = notify;
1559 _pos = 0;
1560 _tries = 0;
1561 _onlyAsFile = false;
1562 _streamType = NP_NORMAL;
1563 _informed = false;
1564 _forceNotify = forceNotify;
1566 // create new stream
1567 _stream = new NPStream;
1568 _stream->ndata = this;
1569 _stream->url = strdup(url.toAscii());
1570 _stream->end = 0;
1571 _stream->pdata = 0;
1572 _stream->lastmodified = 0;
1573 _stream->notifyData = _notifyData;
1575 _mimeType = mimeType;
1577 return true;
1580 void NSPluginStreamBase::updateURL( const KUrl& newURL )
1582 _url = newURL;
1583 free(const_cast<char*>(_stream->url));
1584 _stream->url = strdup(_url.url().toLatin1().data());
1587 int NSPluginStreamBase::process( const QByteArray &data, int start )
1589 int32 max, sent, to_sent, len;
1590 #ifdef __GNUC__
1591 #warning added a const_cast
1592 #endif
1593 char *d = const_cast<char*>(data.data()) + start;
1595 to_sent = data.size() - start;
1596 while (to_sent > 0)
1598 inform();
1600 max = _instance->NPWriteReady(_stream);
1601 //kDebug(1431) << "to_sent == " << to_sent << " and max = " << max;
1602 len = qMin(max, to_sent);
1604 //kDebug(1431) << "-> Feeding stream to plugin: offset=" << _pos << ", len=" << len;
1605 sent = _instance->NPWrite( _stream, _pos, len, d );
1606 //kDebug(1431) << "<- Feeding stream: sent = " << sent;
1608 if (sent == 0) // interrupt the stream for a few ms
1609 break;
1611 if (sent < 0) {
1612 // stream data rejected/error
1613 kDebug(1431) << "stream data rejected/error";
1614 _error = true;
1615 break;
1618 if (_tempFile) {
1619 _tempFile->write(d, sent);
1622 to_sent -= sent;
1623 _pos += sent;
1624 d += sent;
1627 return data.size() - to_sent;
1631 bool NSPluginStreamBase::pump()
1633 //kDebug(1431) << "queue pos " << _queuePos << ", size " << _queue.size();
1635 inform();
1637 if ( _queuePos<_queue.size() ) {
1638 int newPos;
1640 // handle AS_FILE_ONLY streams
1641 if ( _onlyAsFile ) {
1642 if (_tempFile) {
1643 _tempFile->write( _queue, _queue.size() );
1645 newPos = _queuePos+_queue.size();
1646 } else {
1647 // normal streams
1648 newPos = process( _queue, _queuePos );
1651 // count tries
1652 if ( newPos==_queuePos )
1653 _tries++;
1654 else
1655 _tries = 0;
1657 _queuePos = newPos;
1660 // return true if queue finished
1661 return _queuePos>=_queue.size();
1665 void NSPluginStreamBase::queue( const QByteArray &data )
1667 _queue = data;
1668 _queue.detach();
1669 _queuePos = 0;
1670 _tries = 0;
1673 kDebug(1431) << "new queue size=" << data.size()
1674 << " data=" << (void*)data.data()
1675 << " queue=" << (void*)_queue.data() << " qsize="
1676 << _queue.size() << endl;
1681 void NSPluginStreamBase::finish( bool err )
1683 kDebug(1431) << "finish error=" << err;
1685 _queue.resize( 0 );
1686 _pos = 0;
1687 _queuePos = 0;
1689 inform();
1691 if ( !err ) {
1692 if ( _tempFile ) {
1693 _tempFile->close();
1694 _instance->addTempFile( _tempFile );
1695 _tempFile = 0;
1698 if ( !_fileURL.isEmpty() ) {
1699 kDebug() << "stream as file " << _fileURL;
1700 _instance->NPStreamAsFile( _stream, _fileURL.toAscii() );
1703 _instance->NPDestroyStream( _stream, NPRES_DONE );
1704 if (_notifyData || _forceNotify)
1705 _instance->NPURLNotify( _url.url(), NPRES_DONE, _notifyData );
1706 } else {
1707 // close temp file
1708 if ( _tempFile ) {
1709 _tempFile->close();
1712 // destroy stream
1713 _instance->NPDestroyStream( _stream, NPRES_NETWORK_ERR );
1714 if (_notifyData || _forceNotify)
1715 _instance->NPURLNotify( _url.url(), NPRES_NETWORK_ERR, _notifyData );
1718 // delete stream
1719 if (_stream && _stream->url)
1720 free(const_cast<char *>(_stream->url));
1721 delete _stream;
1722 _stream = 0;
1724 // destroy NSPluginStream object
1725 emit finished( this );
1729 /****************************************************************************/
1731 NSPluginBufStream::NSPluginBufStream( class NSPluginInstance *instance )
1732 : NSPluginStreamBase( instance )
1734 _timer = new QTimer( this );
1735 connect( _timer, SIGNAL(timeout()), this, SLOT(timer()) );
1739 NSPluginBufStream::~NSPluginBufStream()
1745 bool NSPluginBufStream::get( const QString& url, const QString& mimeType,
1746 const QByteArray &buf, void *notifyData,
1747 bool singleShot )
1749 _singleShot = singleShot;
1750 if ( create( url, mimeType, notifyData ) ) {
1751 queue( buf );
1752 _timer->setSingleShot( true );
1753 _timer->start( 100 );
1756 return false;
1760 void NSPluginBufStream::timer()
1762 bool finished = pump();
1763 if ( _singleShot )
1764 finish( false );
1765 else {
1767 if ( !finished && tries()<=8 ) {
1768 _timer->setSingleShot( true );
1769 _timer->start( 100 );
1770 } else
1771 finish( error() || tries()>8 );
1777 /****************************************************************************/
1779 NSPluginStream::NSPluginStream( NSPluginInstance *instance )
1780 : NSPluginStreamBase( instance ), _job(0)
1782 _resumeTimer = new QTimer( this );
1783 connect(_resumeTimer, SIGNAL(timeout()), this, SLOT(resume()));
1787 NSPluginStream::~NSPluginStream()
1789 if ( _job )
1790 _job->kill( KJob::Quietly );
1794 bool NSPluginStream::get( const QString& url, const QString& mimeType,
1795 void *notify, bool reload )
1797 // create new stream
1798 if ( create( url, mimeType, notify ) ) {
1799 // start the kio job
1800 _job = KIO::get(KUrl( url ), KIO::NoReload, KIO::HideProgressInfo);
1801 _job->addMetaData("errorPage", "false");
1802 _job->addMetaData("AllowCompressedPage", "false");
1803 if (reload) {
1804 _job->addMetaData("cache", "reload");
1806 connect(_job, SIGNAL(data(KIO::Job *, const QByteArray &)),
1807 SLOT(data(KIO::Job *, const QByteArray &)));
1808 connect(_job, SIGNAL(result(KJob *)), SLOT(result(KJob *)));
1809 connect(_job, SIGNAL(totalSize(KJob *, qulonglong )),
1810 SLOT(totalSize(KJob *, qulonglong)));
1811 connect(_job, SIGNAL(mimetype(KIO::Job *, const QString &)),
1812 SLOT(mimetype(KIO::Job *, const QString &)));
1813 connect(_job, SIGNAL(redirection(KIO::Job *, const KUrl&)),
1814 SLOT(redirection(KIO::Job *, const KUrl&)));
1817 return false;
1821 bool NSPluginStream::post( const QString& url, const QByteArray& data,
1822 const QString& mimeType, void *notify, const KParts::OpenUrlArguments& args,
1823 const KParts::BrowserArguments& browserArgs )
1825 Q_UNUSED( args )
1826 // create new stream
1827 if ( create( url, mimeType, notify ) ) {
1828 // start the kio job
1829 _job = KIO::http_post(KUrl( url ), data, KIO::HideProgressInfo);
1830 _job->addMetaData("content-type", browserArgs.contentType());
1831 _job->addMetaData("errorPage", "false");
1832 _job->addMetaData("AllowCompressedPage", "false");
1833 connect(_job, SIGNAL(data(KIO::Job *, const QByteArray &)),
1834 SLOT(data(KIO::Job *, const QByteArray &)));
1835 connect(_job, SIGNAL(result(KJob *)), SLOT(result(KJob *)));
1836 connect(_job, SIGNAL(totalSize(KJob *, qulonglong )),
1837 SLOT(totalSize(KJob *, qulonglong)));
1838 connect(_job, SIGNAL(mimetype(KIO::Job *, const QString &)),
1839 SLOT(mimetype(KIO::Job *, const QString &)));
1840 connect(_job, SIGNAL(redirection(KIO::Job *, const KUrl&)),
1841 SLOT(redirection(KIO::Job *, const KUrl&)));
1844 return false;
1848 void NSPluginStream::data(KIO::Job*, const QByteArray &data)
1850 //kDebug(1431) << "NSPluginStream::data - job=" << (void*)job << " data size=" << data.size();
1851 queue( data );
1852 if ( !pump() ) {
1853 _job->suspend();
1854 _resumeTimer->setSingleShot( true );
1855 _resumeTimer->start( 100 );
1859 void NSPluginStream::redirection(KIO::Job * /*job*/, const KUrl& url)
1861 updateURL(url);
1864 void NSPluginStream::totalSize(KJob * job, qulonglong size)
1866 kDebug(1431) << "NSPluginStream::totalSize - job=" << (void*)job << " size=" << KIO::number(size);
1867 _stream->end = size;
1870 void NSPluginStream::mimetype(KIO::Job * job, const QString &mimeType)
1872 kDebug(1431) << "NSPluginStream::QByteArray - job=" << (void*)job << " mimeType=" << mimeType;
1873 _mimeType = mimeType;
1879 void NSPluginStream::resume()
1881 if ( error() || tries()>8 ) {
1882 _job->kill( KJob::Quietly );
1883 finish( true );
1884 return;
1887 if ( pump() ) {
1888 kDebug(1431) << "resume job";
1889 _job->resume();
1890 } else {
1891 kDebug(1431) << "restart timer";
1892 _resumeTimer->setSingleShot( true );
1893 _resumeTimer->start( 100 );
1898 void NSPluginStream::result(KJob *job)
1900 int err = job->error();
1901 _job = 0;
1902 finish( err!=0 || error() );
1905 #include "nsplugin.moc"
1906 // vim: ts=4 sw=4 et