diff options
Diffstat (limited to 'khtml/java')
66 files changed, 10146 insertions, 0 deletions
diff --git a/khtml/java/ChangeLog b/khtml/java/ChangeLog new file mode 100644 index 000000000..b276c27ac --- /dev/null +++ b/khtml/java/ChangeLog @@ -0,0 +1,19 @@ +1999-12-14 Richard Moore <rich@ipso-facto.freeserve.co.uk> + + * Added a delay when you reload a class so that a race is less + likely (yuck!) + * Added a destructor to kjaw + * Still has trouble though :-( It only seems to occur whn using + KWin, KWM has no problem. + +Tue Dec 7 23:32:21 GMT 1999 + + * Many improvements to string handling + * Moved to value based collections + * Addition of a d pointer for future binary compatability + * Added some accessor methods + * Improvements to kjavaprocess + * - Now handles system property settings + * - Detects death of the server + * - Supports bidirectional comms + * Applets and contexts are destroyed properly diff --git a/khtml/java/KJAS_GRAMMAR.txt b/khtml/java/KJAS_GRAMMAR.txt new file mode 100644 index 000000000..5632f8de8 --- /dev/null +++ b/khtml/java/KJAS_GRAMMAR.txt @@ -0,0 +1,82 @@ +This is documentation for the updated KJAS protocol. + +KJAS Grammar +=============================================================================== + +## Commands From KAppletWidget(C++) to KJAS(Java Process) +<KJAS Command> -> <CMD Length><CMD> +<CMD Length> -> <StringNum> +<CMD> -> <createContext> | + <destroyContext> | + <createApplet> | + <destroyApplet> | + <startApplet> | + <stopApplet> | + <initApplet> | + <showURLInFrame> | + <showDocument> | + <showStatus> | + <resizeApplet> | + <getURLData> | + <URLData> | + <shutDownServer> + +<createContext> -> <1 byte equal to 1 when cast as int><SEP><ContextID><END> +<destroyContext> -> <1 byte equal to 2 when cast as int><SEP><ContextID><END> + +<createApplet> -> <1 byte equal to 3 when cast as int><SEP><ContextID> + <SEP><AppletID><SEP><AppletName><SEP><ClassName><SEP> + <BaseURL><SEP><CodeBase><SEP><Archives> + <SEP><Width><SEP><Height><SEP><WindowTitle><SEP><ParamList> +<destroyApplet> -> <1 byte equal to 4 when cast as int><SEP><ContextID> + <SEP><AppletID><END> +<startApplet> -> <1 byte equal to 5 when cast as int><SEP><ContextID> + <SEP><AppletID><END> +<stopApplet> -> <1 byte equal to 6 when cast as int><SEP><ContextID> + <SEP><AppletID><END> +<initApplet> -> <1 byte equal to 7 when cast as int><SEP><ContextID> + <SEP><AppletID><END> + + +## Commands from KJAS(Java Process) to KAppletWidget(C++) +<showDocument> -> <1 byte equal to 8 when cast as int><SEP><ContextID> + <SEP><URL><END> +<showURLInFrame> -> <1 byte equal to 9 when cast as int><SEP><ContextID> + <SEP><URL><SEP><targetFrame><END> +<showStatus> -> <1 byte equal to 10 when cast as int><SEP><ContextID> + <SEP><string><END> +<resizeApplet> -> <1 byte equal to 11 when cast as int><SEP><ContextID> + <SEP><AppletID><SEP><Width><SEP><Height><END> +<getURLData> -> <1 byte equal to 12 when cast as int><SEP><ClassLoaderID> + <SEP><URL><END> +<URLData> -> <1 byte equal to 13 when cast as int><SEP><ClassLoaderID> + <SEP><URL><SEP><DATA><END> + +<shutDownServer> -> <1 byte equal to 14 when cast as int><END> + +## basic data types +<CMD Length> -> <StringNum> +<ContextID> -> string +<AppletID> -> string +<AppletName> -> string +<ParamList> -> <StringNum><SEP><ParamPairList> +<ParamPairList> -> StringNum of ParamPair +<ParamPair> -> <ParamName><SEP><ParamValue><SEP> +<ClassName> -> string +<BaseURL> -> <URL> +<CodeBase> -> <URL> +<Archives> -> string (list of jarfile names) +<Width> -> string representation of integer +<Height> -> string representation of integer +<Title> -> string +<ParamName> -> string +<ParamValue> -> string +<Host> -> string (must be a valid URL) +<URL> -> string (must be a valid URL) +<targetFrame> -> string +<WindowTitle> -> string +<END> -> <SEP> +<SEP> -> Null character- 1 byte = 0 +<StringNum> -> padded string representation of integer, 8 characters long +<ClassLoaderID> -> string +<DATA> -> byte array diff --git a/khtml/java/Makefile.am b/khtml/java/Makefile.am new file mode 100644 index 000000000..b29e2349c --- /dev/null +++ b/khtml/java/Makefile.am @@ -0,0 +1,37 @@ +KDE_CXXFLAGS = $(WOVERLOADED_VIRTUAL) + +noinst_LTLIBRARIES = libkjava.la +libkjava_la_SOURCES = kjavaapplet.cpp kjavaappletcontext.cpp \ + kjavaappletserver.cpp kjavaappletwidget.cpp kjavaprocess.cpp \ + kjavadownloader.cpp + +noinst_HEADERS = kjavaappletwidget.h kqeventutil.h kxeventutil.h \ + kjavaapplet.h kjavaappletcontext.h \ + kjavaappletserver.h kjavaprocess.h kjavaappletviewer.h + +METASOURCES = AUTO + +libkjava_la_LDFLAGS = $(KDE_MT_LDFLAGS) -no-undefined +libkjava_la_LIBADD = $(LIB_KPARTS) + +INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/khtml \ + -I$(top_srcdir)/kio/kssl -I$(top_builddir)/kio/kssl \ + $(all_includes) + +kjavadata_DATA = kjava.jar kjava.policy pluginsinfo +kjavadatadir = $(kde_datadir)/kjava/ + +kjavaiconsdir = $(kde_datadir)/kjava/icons +kjavaicons_ICON = AUTO + +SUBDIRS = . tests dummy + +kde_module_LTLIBRARIES= kjavaappletviewer.la +kjavaappletviewer_la_SOURCES= kjavaapplet.cpp kjavaappletcontext.cpp \ + kjavaappletserver.cpp kjavaappletwidget.cpp kjavaprocess.cpp \ + kjavadownloader.cpp kjavaappletviewer.cpp + +kjavaappletviewer_la_LDFLAGS= $(all_libraries) -module $(KDE_PLUGIN) +kjavaappletviewer_la_LIBADD= $(LIB_KPARTS) + +kde_services_DATA= kjavaappletviewer.desktop diff --git a/khtml/java/README b/khtml/java/README new file mode 100644 index 000000000..5d64e742f --- /dev/null +++ b/khtml/java/README @@ -0,0 +1,20 @@ +Wynn Wilkes- November 14, 2000 +I've just completed a large update that fixes a large number of bugs. The +update also adds applet security. The security update requires a Java 2 +jvm. + +This is libkdejava, the KDE Java support library. + +Directory map: + +$CWD CPP sources for KDE binding to KJAS and some + additional utility classes. +kjava-classes.zip An *uncompressed* ZIP file containing the .class files + for the KJAS server. The files must be uncompressed to + support some crappy JVMs. +org/kde/kjas/server Java sources for KJAS server process + +You can find more information at http://developer.kde.org/language-bindings/java/ + +Richard Moore. +rich@kde.org diff --git a/khtml/java/TODO b/khtml/java/TODO new file mode 100644 index 000000000..2b9289e59 --- /dev/null +++ b/khtml/java/TODO @@ -0,0 +1,22 @@ +Wynn Wilkes (November 14, 2000) +As of now, KJAS requires a Java 2 platform for classloading +and the default security manager. If anyone wants to implement +a Java 1.1 security manager, please feel free and we can integrate +both versions. + +- Get the keyboard focus issues fixed +- Fix khtml_part to have one applet context per Document +- add a context cache someplace so we can reload contexts- + this will keep us from having to restart the jvm over + and over- this is the biggest performance problem I think +- fix khtml_part so it will start and stop applets?? +- Implement class loading via html proxies if one is set + + +- Use of QGuardedPointer +- LiveScript support +- Custom applet types +- Better support for Java 2 + - Use a factory to create the classloader and security managers + - Use URLClassLoader- this is done +- Support for KIO URLs diff --git a/khtml/java/build.properties b/khtml/java/build.properties new file mode 100644 index 000000000..c2c0016bc --- /dev/null +++ b/khtml/java/build.properties @@ -0,0 +1,9 @@ +dest=. +jar=kjava +build=classes +src=. +includes=netscape/**,org/** +excludes=* +images=images +debug=true +debuglevel=lines,vars,source diff --git a/khtml/java/build.xml b/khtml/java/build.xml new file mode 100644 index 000000000..16b523588 --- /dev/null +++ b/khtml/java/build.xml @@ -0,0 +1,49 @@ +<?xml version="1.0"?> + +<project name="KJAS" basedir="." default="all"> + + <property file="build.properties"/> + + <target name="init"> + <mkdir dir="${build}" /> + </target> + + <target name="compile" depends="init"> + <javac srcdir="${src}" destdir="${build}" includes="${includes}" excludes="${excludes}" deprecation="true" debug="${debug}" debuglevel="${debuglevel}" source="1.3" target="1.2"/> + </target> + + <target name="images" depends="init"> + <mkdir dir="${build}/images"/> + <copy todir="${build}/images"> + <fileset dir="${images}"> + <include name="*gif"/> + </fileset> + </copy> + </target> + + <target name="jar" depends="init,images,compile"> + <jar jarfile="${jar}.jar" compress="false" basedir="${build}" /> + </target> + + <target name="all" depends="jar" description="Build everything."> + <echo message="Application built." /> + </target> + + <target name="clean" depends="init" description="Clean all build products."> + <delete file="${jar}.jar" /> + <delete dir="${build}" /> + </target> + + <target name="test-init" depends=""> + <mkdir dir="tests/classes" /> + </target> + + <target name="test" depends="test-init" description="Build the test applets"> + <javac srcdir="tests" destdir="tests/classes" debug="true" deprecation="true" source="1.3" target="1.2"/> + </target> + + <target name="test-clean" depends=""> + <delete dir="tests/classes" /> + </target> + +</project> diff --git a/khtml/java/configure.in.in b/khtml/java/configure.in.in new file mode 100644 index 000000000..32e7883ef --- /dev/null +++ b/khtml/java/configure.in.in @@ -0,0 +1,5 @@ +dnl don't remove +dnl AC_OUTPUT(khtml/java/kjava.policy) +KJAVA_POLICYPATH=${kde_datadir}/kjava/- +KDE_EXPAND_MAKEVAR(KJAVA_POLICYPATH, KJAVA_POLICYPATH) +AC_SUBST(KJAVA_POLICYPATH) diff --git a/khtml/java/cr16-action-java.png b/khtml/java/cr16-action-java.png Binary files differnew file mode 100644 index 000000000..dca85a129 --- /dev/null +++ b/khtml/java/cr16-action-java.png diff --git a/khtml/java/dummy/Makefile.am b/khtml/java/dummy/Makefile.am new file mode 100644 index 000000000..5438da6ad --- /dev/null +++ b/khtml/java/dummy/Makefile.am @@ -0,0 +1,13 @@ + +lib_LTLIBRARIES = libkjava.la +libkjava_la_SOURCES = dummy.cpp + +dummy.cpp: + echo "#ifdef _AIX" > $@ + echo "namespace {" >> $@ + echo "void *not_empty_file;" >> $@ + echo "}" >> $@ + echo "#endif" >> $@ + +libkjava_la_LDFLAGS = -version-info 1:0 + diff --git a/khtml/java/images/animbean.gif b/khtml/java/images/animbean.gif Binary files differnew file mode 100644 index 000000000..a842be25d --- /dev/null +++ b/khtml/java/images/animbean.gif diff --git a/khtml/java/images/beanicon.png b/khtml/java/images/beanicon.png Binary files differnew file mode 100644 index 000000000..d1632c16e --- /dev/null +++ b/khtml/java/images/beanicon.png diff --git a/khtml/java/images/brokenbean.gif b/khtml/java/images/brokenbean.gif Binary files differnew file mode 100644 index 000000000..9f46dc355 --- /dev/null +++ b/khtml/java/images/brokenbean.gif diff --git a/khtml/java/kjava.jar b/khtml/java/kjava.jar Binary files differnew file mode 100644 index 000000000..afab6884b --- /dev/null +++ b/khtml/java/kjava.jar diff --git a/khtml/java/kjava.policy.in b/khtml/java/kjava.policy.in new file mode 100644 index 000000000..72197bb2e --- /dev/null +++ b/khtml/java/kjava.policy.in @@ -0,0 +1,8 @@ +grant codeBase "file:@KJAVA_POLICYPATH@" +{ + permission java.security.AllPermission; +}; +grant { + permission java.AudioPermission "play"; + permission java.lang.RuntimePermission "accessClassInPackage.sun.audio"; +}; diff --git a/khtml/java/kjavaapplet.cpp b/khtml/java/kjavaapplet.cpp new file mode 100644 index 000000000..2269bc756 --- /dev/null +++ b/khtml/java/kjavaapplet.cpp @@ -0,0 +1,288 @@ +/* This file is part of the KDE project + * + * Copyright (C) 2000 Richard Moore <rich@kde.org> + * 2000 Wynn Wilkes <wynnw@caldera.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kjavaappletwidget.h" +#include "kjavaappletcontext.h" + +#include <klocale.h> +#include <kdebug.h> +#include <kparts/browserextension.h> + + + +class KJavaAppletPrivate +{ +public: + bool reallyExists; + QString className; + QString appName; + QString baseURL; + QString codeBase; + QString archives; + QSize size; + QString windowName; + KJavaApplet::AppletState state; + bool failed; + + KJavaAppletWidget* UIwidget; +}; + + +KJavaApplet::KJavaApplet( KJavaAppletWidget* _parent, + KJavaAppletContext* _context ) + : params() +{ + d = new KJavaAppletPrivate; + + d->UIwidget = _parent; + d->state = UNKNOWN; + d->failed = false; + + if( _context ) + setAppletContext( _context ); + + d->reallyExists = false; +} + +KJavaApplet::~KJavaApplet() +{ + if ( d->reallyExists ) + context->destroy( this ); + + delete d; +} + +bool KJavaApplet::isCreated() +{ + return d->reallyExists; +} + +void KJavaApplet::setAppletContext( KJavaAppletContext* _context ) +{ + context = _context; + context->registerApplet( this ); +} + +void KJavaApplet::setAppletClass( const QString& _className ) +{ + d->className = _className; +} + +QString& KJavaApplet::appletClass() +{ + return d->className; +} + +QString& KJavaApplet::parameter( const QString& name ) +{ + return params[ name ]; +} + +void KJavaApplet::setParameter( const QString& name, const QString& value ) +{ + params.insert( name, value ); +} + +QMap<QString,QString>& KJavaApplet::getParams() +{ + return params; +} + +void KJavaApplet::setBaseURL( const QString& baseURL ) +{ + d->baseURL = baseURL; +} + +QString& KJavaApplet::baseURL() +{ + return d->baseURL; +} + +void KJavaApplet::setCodeBase( const QString& codeBase ) +{ + d->codeBase = codeBase; +} + +QString& KJavaApplet::codeBase() +{ + return d->codeBase; +} + +void KJavaApplet::setSize( QSize size ) +{ + d->size = size; +} + +QSize KJavaApplet::size() +{ + return d->size; +} + +void KJavaApplet::setArchives( const QString& _archives ) +{ + d->archives = _archives; +} + +QString& KJavaApplet::archives() +{ + return d->archives; +} + +void KJavaApplet::resizeAppletWidget( int width, int height ) +{ + kdDebug(6100) << "KJavaApplet, id = " << id << ", ::resizeAppletWidget to " << width << ", " << height << endl; + + QStringList sl; + sl.push_back( QString::number( 0 ) ); // applet itself has id 0 + sl.push_back( QString( "eval" ) ); // evaluate next script + sl.push_back( QString::number( KParts::LiveConnectExtension::TypeString ) ); + sl.push_back( QString( "this.setAttribute('WIDTH',%1);this.setAttribute('HEIGHT',%2)" ).arg( width ).arg( height ) ); + jsData( sl ); +} + +void KJavaApplet::setAppletName( const QString& name ) +{ + d->appName = name; +} + +void KJavaApplet::setWindowName( const QString& title ) +{ + d->windowName = title; +} + +QString& KJavaApplet::getWindowName() +{ + return d->windowName; +} + +QString& KJavaApplet::appletName() +{ + return d->appName; +} + +void KJavaApplet::create( ) +{ + if ( !context->create( this ) ) + setFailed(); + d->reallyExists = true; +} + +void KJavaApplet::init() +{ + context->init( this ); +} + +void KJavaApplet::start() +{ + context->start( this ); +} + +void KJavaApplet::stop() +{ + context->stop( this ); +} + +int KJavaApplet::appletId() +{ + return id; +} + +void KJavaApplet::setAppletId( int _id ) +{ + id = _id; +} + +void KJavaApplet::stateChange( const int newStateInt ) { + AppletState newState = (AppletState)newStateInt; + bool ok = false; + if (d->failed) { + return; + } + switch ( newState ) { + case CLASS_LOADED: + ok = (d->state == UNKNOWN); + break; + case INSTANCIATED: + if (ok) { + showStatus(i18n("Initializing Applet \"%1\"...").arg(appletName())); + } + ok = (d->state == CLASS_LOADED); + break; + case INITIALIZED: + ok = (d->state == INSTANCIATED); + if (ok) { + showStatus(i18n("Starting Applet \"%1\"...").arg(appletName())); + start(); + } + break; + case STARTED: + ok = (d->state == INITIALIZED || d->state == STOPPED); + if (ok) { + showStatus(i18n("Applet \"%1\" started").arg(appletName())); + } + break; + case STOPPED: + ok = (d->state == INITIALIZED || d->state == STARTED); + if (ok) { + showStatus(i18n("Applet \"%1\" stopped").arg(appletName())); + } + break; + case DESTROYED: + ok = true; + break; + default: + break; + } + if (ok) { + d->state = newState; + } else { + kdError(6100) << "KJavaApplet::stateChange : don't want to switch from state " + << d->state << " to " << newState << endl; + } +} + +void KJavaApplet::showStatus(const QString &msg) { + QStringList args; + args << msg; + context->processCmd("showstatus", args); +} + +void KJavaApplet::setFailed() { + d->failed = true; +} + +bool KJavaApplet::isAlive() const { + return ( + !d->failed + && d->state >= INSTANCIATED + && d->state < STOPPED + ); +} + +KJavaApplet::AppletState KJavaApplet::state() const { + return d->state; +} + +bool KJavaApplet::failed() const { + return d->failed; +} + +#include "kjavaapplet.moc" diff --git a/khtml/java/kjavaapplet.h b/khtml/java/kjavaapplet.h new file mode 100644 index 000000000..abde99927 --- /dev/null +++ b/khtml/java/kjavaapplet.h @@ -0,0 +1,251 @@ +// -*- c++ -*- +/* This file is part of the KDE project + * + * Copyright (C) 2000 Richard Moore <rich@kde.org> + * 2000 Wynn Wilkes <wynnw@caldera.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef KJAVAAPPLET_H +#define KJAVAAPPLET_H + +#include <kurl.h> + +#include <qobject.h> +#include <qstringlist.h> +#include <qmap.h> + +/** + * @short A Java applet + * + * This class encapsulates the data the Applet Server needs to load + * the Applet class files, and set the proper size of the Applet. It also + * has an interface for applets to resize themselves. + * + * @author Richard J. Moore, rich@kde.org + * @author Wynn Wilkes, wynnw@kde.org + */ + +class KJavaApplet; +class KJavaAppletWidget; +class KJavaAppletContext; +class KJavaAppletPrivate; + + +class KJavaApplet : public QObject +{ +Q_OBJECT + +public: + // states describing the life cycle of an applet. + // keep in sync with applet state in KJASAppletStub.java ! + typedef enum { + UNKNOWN = 0, + CLASS_LOADED = 1, + INSTANCIATED = 2, + INITIALIZED = 3, + STARTED = 4, + STOPPED = 5, + DESTROYED = 6 + } AppletState; + KJavaApplet( KJavaAppletWidget* _parent, KJavaAppletContext* _context = 0 ); + ~KJavaApplet(); + + /** + * Set the applet context'. + */ + void setAppletContext( KJavaAppletContext* _context ); + + /** + * Specify the name of the class file to run. For example 'Lake.class'. + */ + void setAppletClass( const QString& clazzName ); + + /** + * Get the name of the Class file the applet should run + */ + QString& appletClass(); + + /** + * Set the URL of the document embedding the applet. + */ + void setBaseURL( const QString& base ); + + /** + * get the Base URL of the document embedding the applet + */ + QString& baseURL(); + + /** + * Set the codebase of the applet classes. + */ + void setCodeBase( const QString& codeBase ); + + /** + * Get the codebase of the applet classes + */ + QString& codeBase(); + + /** + * Set the list of archives at the Applet's codebase to search in for + * class files and other resources + */ + void setArchives( const QString& _archives ); + + /** + * Get the list of Archives that should be searched for class files + * and other resources + */ + QString& archives(); + + /** + * Set the name the applet should be called in its context + */ + void setAppletName( const QString& name ); + + /** + * Get the name the applet should be called in its context + */ + QString& appletName(); + + /** + * Set the size of the applet + */ + void setSize( QSize size ); + + /** + * Get the size of the applet + */ + QSize size(); + + /** + * Specify a parameter to be passed to the applet. + */ + void setParameter( const QString& name, const QString& value ); + + /** + * Look up the parameter value for the given Parameter. Returns + * QString::null if the name has not been set. + */ + QString& parameter( const QString& name ); + + /** + * Get a reference to the Paramaters and their values + */ + QMap<QString,QString>& getParams(); + + /** + * Set the window title for swallowing + */ + void setWindowName( const QString& title ); + + /** + * Get the window title this applet should use + */ + QString& getWindowName(); + + /** + * Interface for applets to resize themselves + */ + void resizeAppletWidget( int width, int height ); + + /** + * Send message to AppletServer to create this applet's + * frame to be swallowed and download the applet classes + */ + void create(); + + /** + * Send message to AppletServer to Initialize and show + * this applet + */ + void init(); + + /** + * Returns status of applet- whether it's been created or not + */ + bool isCreated(); + + /** + * Run the applet. + */ + void start(); + + /** + * Pause the applet. + */ + void stop(); + + /** + * Returns the unique ID this applet is given + */ + int appletId(); + + /** + * Set the applet ID. + */ + void setAppletId( int id ); + + KJavaAppletContext* getContext() const { return context; } + + /** + * Get/Set the user name + */ + void setUser(const QString & _user) { username = _user; } + const QString & user () const { return username; } + + /** + * Get/Set the user password + */ + void setPassword(const QString & _password) { userpassword = _password; } + const QString & password () const { return userpassword; } + + /** + * Get/Set the auth name + */ + void setAuthName(const QString & _auth) { authname = _auth; } + const QString & authName () const { return authname; } + + /** + * called from the protocol engine + * changes the status according to the one on the java side. + * Do not call this yourself! + */ + void stateChange ( const int newState ); + void setFailed (); + AppletState state() const; + bool failed() const; + bool isAlive() const; + /** + * JavaScript coming from Java + **/ + void jsData (const QStringList & args) { emit jsEvent (args); } +signals: + void jsEvent (const QStringList & args); +private: + void showStatus( const QString &msg); + KJavaAppletPrivate* d; + QMap<QString, QString> params; + KJavaAppletContext* context; + int id; + QString username; + QString userpassword; + QString authname; +}; + +#endif // KJAVAAPPLET_H diff --git a/khtml/java/kjavaappletcontext.cpp b/khtml/java/kjavaappletcontext.cpp new file mode 100644 index 000000000..94e17ff72 --- /dev/null +++ b/khtml/java/kjavaappletcontext.cpp @@ -0,0 +1,274 @@ +/* This file is part of the KDE project + * + * Copyright (C) 2000 Richard Moore <rich@kde.org> + * 2000 Wynn Wilkes <wynnw@caldera.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kjavaappletcontext.h" +#include "kjavaappletserver.h" +#include "kjavaprocess.h" +#include "kjavaapplet.h" +#include <klocale.h> +#include <kmessagebox.h> +#include <kdebug.h> +#include <qmap.h> +#include <qguardedptr.h> +#include <qstringlist.h> +#include <qregexp.h> + +// This file was using 6002, but kdebug.areas didn't know about that number +#define DEBUGAREA 6100 + +typedef QMap< int, QGuardedPtr<KJavaApplet> > AppletMap; + +// For future expansion +class KJavaAppletContextPrivate +{ +friend class KJavaAppletContext; +private: + AppletMap applets; +}; + +// Static Factory Functions +int KJavaAppletContext::contextCount = 0; + +/* Class Implementation + */ +KJavaAppletContext::KJavaAppletContext() + : QObject() +{ + d = new KJavaAppletContextPrivate; + server = KJavaAppletServer::allocateJavaServer(); + connect(server->javaProcess(), SIGNAL(exited(int)), this, SLOT(javaProcessExited(int))); + + id = contextCount; + server->createContext( id, this ); + + ++contextCount; +} + +KJavaAppletContext::~KJavaAppletContext() +{ + server->destroyContext( id ); + KJavaAppletServer::freeJavaServer(); + delete d; +} + +int KJavaAppletContext::contextId() +{ + return id; +} + +void KJavaAppletContext::setContextId( int _id ) +{ + id = _id; +} + +void KJavaAppletContext::registerApplet( KJavaApplet* applet ) +{ + static int appletId = 0; + + applet->setAppletId( ++appletId ); + d->applets.insert( appletId, applet ); +} + +bool KJavaAppletContext::create( KJavaApplet* applet ) +{ + return server->createApplet( id, applet->appletId(), + applet->appletName(), + applet->appletClass(), + applet->baseURL(), + applet->user(), + applet->password(), + applet->authName(), + applet->codeBase(), + applet->archives(), + applet->size(), + applet->getParams(), + applet->getWindowName() ); + + +} + +void KJavaAppletContext::destroy( KJavaApplet* applet ) +{ + const int appletId = applet->appletId(); + d->applets.remove( appletId ); + + server->destroyApplet( id, appletId ); +} + +void KJavaAppletContext::init( KJavaApplet* applet ) +{ + server->initApplet( id, applet->appletId() ); +} + +void KJavaAppletContext::start( KJavaApplet* applet ) +{ + server->startApplet( id, applet->appletId() ); +} + +void KJavaAppletContext::stop( KJavaApplet* applet ) +{ + server->stopApplet( id, applet->appletId() ); +} + +void KJavaAppletContext::processCmd( QString cmd, QStringList args ) +{ + received( cmd, args ); +} + +void KJavaAppletContext::received( const QString& cmd, const QStringList& arg ) +{ + kdDebug(6100) << "KJavaAppletContext::received, cmd = >>" << cmd << "<<" << endl; + kdDebug(6100) << "arg count = " << arg.count() << endl; + + if ( cmd == QString::fromLatin1("showstatus") + && !arg.empty() ) + { + QString tmp = arg.first(); + tmp.replace(QRegExp("[\n\r]"), ""); + kdDebug(6100) << "status message = " << tmp << endl; + emit showStatus( tmp ); + } + else if ( cmd == QString::fromLatin1( "showurlinframe" ) + && arg.count() > 1 ) + { + kdDebug(6100) << "url = " << arg[0] << ", frame = " << arg[1] << endl; + emit showDocument( arg[0], arg[1] ); + } + else if ( cmd == QString::fromLatin1( "showdocument" ) + && !arg.empty() ) + { + kdDebug(6100) << "url = " << arg.first() << endl; + emit showDocument( arg.first(), "_top" ); + } + else if ( cmd == QString::fromLatin1( "resizeapplet" ) + && arg.count() > 2 ) + { + //arg[1] should be appletID + //arg[2] should be new width + //arg[3] should be new height + bool ok; + const int appletID = arg[0].toInt( &ok ); + const int width = arg[1].toInt( &ok ); + const int height = arg[2].toInt( &ok ); + + if( !ok ) + { + kdError(DEBUGAREA) << "could not parse out parameters for resize" << endl; + } + else + { + KJavaApplet* const tmp = d->applets[appletID]; + if (tmp) + tmp->resizeAppletWidget( width, height ); + } + } + else if (cmd.startsWith(QString::fromLatin1("audioclip_"))) { + kdDebug(DEBUGAREA) << "process Audio command (not yet implemented): " << cmd << " " << arg[0] << endl; + } + else if ( cmd == QString::fromLatin1( "JS_Event" ) + && arg.count() > 2 ) + { + bool ok; + const int appletID = arg.first().toInt(&ok); + KJavaApplet * applet; + if (ok && (applet = d->applets[appletID])) + { + QStringList js_args(arg); + js_args.pop_front(); + applet->jsData(js_args); + } + else + kdError(DEBUGAREA) << "parse JS event " << arg[0] << " " << arg[1] << endl; + } + else if ( cmd == QString::fromLatin1( "AppletStateNotification" ) ) + { + bool ok; + const int appletID = arg.first().toInt(&ok); + if (ok) + { + KJavaApplet* const applet = d->applets[appletID]; + if ( applet ) + { + const int newState = arg[1].toInt(&ok); + if (ok) + { + applet->stateChange(newState); + if (newState == KJavaApplet::INITIALIZED) { + kdDebug(DEBUGAREA) << "emit appletLoaded" << endl; + emit appletLoaded(); + } + } else + kdError(DEBUGAREA) << "AppletStateNotification: status is not numerical" << endl; + } else + kdWarning(DEBUGAREA) << "AppletStateNotification: No such Applet with ID=" << arg[0] << endl; + } else + kdError(DEBUGAREA) << "AppletStateNotification: Applet ID is not numerical" << endl; + } + else if ( cmd == QString::fromLatin1( "AppletFailed" ) ) { + bool ok; + const int appletID = arg.first().toInt(&ok); + if (ok) + { + KJavaApplet* const applet = d->applets[appletID]; + /* + QString errorDetail(arg[1]); + errorDetail.replace(QRegExp(":\\s*"), ":\n"); + KMessageBox::detailedError(0L, i18n("Java error while loading applet."), errorDetail); + */ + if (applet) + applet->setFailed(); + emit appletLoaded(); + } + } +} + +void KJavaAppletContext::javaProcessExited(int) { + AppletMap::iterator it = d->applets.begin(); + const AppletMap::iterator itEnd = d->applets.end(); + for (; it != itEnd; ++it) + if (!(*it).isNull() && (*it)->isCreated() && !(*it)->failed()) { + (*it)->setFailed(); + if ((*it)->state() < KJavaApplet::INITIALIZED) + emit appletLoaded(); + } +} + +bool KJavaAppletContext::getMember(QStringList & args, QStringList & ret_args) { + args.push_front( QString::number(id) ); + return server->getMember( args, ret_args ); +} + +bool KJavaAppletContext::putMember( QStringList & args ) { + args.push_front( QString::number(id) ); + return server->putMember( args ); +} + +bool KJavaAppletContext::callMember(QStringList & args, QStringList &ret_args) { + args.push_front( QString::number(id) ); + return server->callMember( args, ret_args ); +} + +void KJavaAppletContext::derefObject( QStringList & args ) { + args.push_front( QString::number(id) ); + server->derefObject( args ); +} + +#include <kjavaappletcontext.moc> diff --git a/khtml/java/kjavaappletcontext.h b/khtml/java/kjavaappletcontext.h new file mode 100644 index 000000000..06e5aafc7 --- /dev/null +++ b/khtml/java/kjavaappletcontext.h @@ -0,0 +1,141 @@ +// -*- c++ -*- + +/* This file is part of the KDE project + * + * Copyright (C) 2000 Richard Moore <rich@kde.org> + * 2000 Wynn Wilkes <wynnw@caldera.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KJAVAAPPLETCONTEXT_H +#define KJAVAAPPLETCONTEXT_H + +#include <qobject.h> + +/** + * @short Provides a context for KJavaAppletWidgets + * + * Applets run in a context- (see the Java documentation for more information + * on contexts). Currently, each document in KHTML creates one context, in + * which multiple applets can run. + * + * @author Richard J. Moore, rich@kde.org + * @author Wynn Wilkes, wynnw@caldera.com + */ + + +class KJavaAppletServer; +class KJavaApplet; +class KJavaAppletContextPrivate; + +class KJavaAppletContext : public QObject +{ +Q_OBJECT + +public: + KJavaAppletContext(); + ~KJavaAppletContext(); + + /** + * Returns the ID of this context. + */ + int contextId(); + + /** + * Sets the ID of this context. + */ + void setContextId( int id ); + + /** + * registers applet + **/ + void registerApplet( KJavaApplet* ); + + /** + * Sends a message to create the applet. + */ + bool create( KJavaApplet* ); + + /** + * Sends a message to destroy the applet. + */ + void destroy( KJavaApplet* ); + + /** + * Sends a message to initialize the applet. + */ + void init( KJavaApplet* ); + + /** + * Sends a message to start the applet. + */ + void start( KJavaApplet* ); + + /** + * Sends a message to stop the applet. + */ + void stop( KJavaApplet* ); + + /** + * use this for applet call backs, the AppletServer + * calls this directly. + */ + void processCmd( QString cmd, QStringList args ); + + /** + * LiveConnect functions + */ + bool getMember(QStringList & args, QStringList & ret_args); + bool putMember(QStringList & args); + bool callMember(QStringList & args, QStringList & ret_args); + void derefObject(QStringList & args); + + KJavaAppletServer* getServer() const { return server; } +signals: + /** + * Signals the KHMTL Part to show this as the status message. + */ + void showStatus ( const QString& txt ); + + /** + * Signals the KHTML Part to show a url in a given target + */ + void showDocument( const QString& url, const QString& target ); + + /** + * Signals the KHTML Part an applet is loaded + **/ + void appletLoaded(); + +protected: + //The counter to generate ID's for the contexts + static int contextCount; + + // The applet server this context is attached to. + KJavaAppletServer* server; + +protected slots: + void received( const QString& cmd, const QStringList& arg ); + void javaProcessExited(int); + +private: + int id; + KJavaAppletContextPrivate* d; + +}; + +#endif // KJAVAAPPLETCONTEXT_H diff --git a/khtml/java/kjavaappletserver.cpp b/khtml/java/kjavaappletserver.cpp new file mode 100644 index 000000000..e766dcb5b --- /dev/null +++ b/khtml/java/kjavaappletserver.cpp @@ -0,0 +1,833 @@ +/* This file is part of the KDE project + * + * Copyright (C) 2000 Richard Moore <rich@kde.org> + * 2000 Wynn Wilkes <wynnw@caldera.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include "kjavaappletserver.h" +#include "kjavaappletcontext.h" +#include "kjavaprocess.h" +#include "kjavadownloader.h" + +#include <kdebug.h> +#include <kconfig.h> +#include <klocale.h> +#include <kparts/browserextension.h> +#include <kapplication.h> +#include <kstandarddirs.h> + +#include <kio/job.h> +#include <kio/kprotocolmanager.h> +#include <ksslcertificate.h> +#include <ksslcertchain.h> +#include <kssl.h> + +#include <qtimer.h> +#include <qguardedptr.h> +#include <qvaluelist.h> +#include <qptrlist.h> +#include <qdir.h> +#include <qeventloop.h> +#include <qapplication.h> +#include <qlabel.h> +#include <qdialog.h> +#include <qpushbutton.h> +#include <qlayout.h> +#include <qregexp.h> + +#include <stdlib.h> +#include <assert.h> + +#define KJAS_CREATE_CONTEXT (char)1 +#define KJAS_DESTROY_CONTEXT (char)2 +#define KJAS_CREATE_APPLET (char)3 +#define KJAS_DESTROY_APPLET (char)4 +#define KJAS_START_APPLET (char)5 +#define KJAS_STOP_APPLET (char)6 +#define KJAS_INIT_APPLET (char)7 +#define KJAS_SHOW_DOCUMENT (char)8 +#define KJAS_SHOW_URLINFRAME (char)9 +#define KJAS_SHOW_STATUS (char)10 +#define KJAS_RESIZE_APPLET (char)11 +#define KJAS_GET_URLDATA (char)12 +#define KJAS_URLDATA (char)13 +#define KJAS_SHUTDOWN_SERVER (char)14 +#define KJAS_JAVASCRIPT_EVENT (char)15 +#define KJAS_GET_MEMBER (char)16 +#define KJAS_CALL_MEMBER (char)17 +#define KJAS_PUT_MEMBER (char)18 +#define KJAS_DEREF_OBJECT (char)19 +#define KJAS_AUDIOCLIP_PLAY (char)20 +#define KJAS_AUDIOCLIP_LOOP (char)21 +#define KJAS_AUDIOCLIP_STOP (char)22 +#define KJAS_APPLET_STATE (char)23 +#define KJAS_APPLET_FAILED (char)24 +#define KJAS_DATA_COMMAND (char)25 +#define KJAS_PUT_URLDATA (char)26 +#define KJAS_PUT_DATA (char)27 +#define KJAS_SECURITY_CONFIRM (char)28 +#define KJAS_SHOW_CONSOLE (char)29 + + +class JSStackFrame; + +typedef QMap< int, KJavaKIOJob* > KIOJobMap; +typedef QMap< int, JSStackFrame* > JSStack; + +class JSStackFrame { +public: + JSStackFrame(JSStack & stack, QStringList & a) + : jsstack(stack), args(a), ticket(counter++), ready(false), exit (false) { + jsstack.insert( ticket, this ); + } + ~JSStackFrame() { + jsstack.erase( ticket ); + } + JSStack & jsstack; + QStringList & args; + int ticket; + bool ready : 1; + bool exit : 1; + static int counter; +}; + +int JSStackFrame::counter = 0; + +class KJavaAppletServerPrivate +{ +friend class KJavaAppletServer; +private: + KJavaAppletServerPrivate() : kssl( 0L ) {} + ~KJavaAppletServerPrivate() { + delete kssl; + } + int counter; + QMap< int, QGuardedPtr<KJavaAppletContext> > contexts; + QString appletLabel; + JSStack jsstack; + KIOJobMap kiojobs; + bool javaProcessFailed; + bool useKIO; + KSSL * kssl; + //int locked_context; + //QValueList<QByteArray> java_requests; +}; + +static KJavaAppletServer* self = 0; + +KJavaAppletServer::KJavaAppletServer() +{ + d = new KJavaAppletServerPrivate; + process = new KJavaProcess(); + + connect( process, SIGNAL(received(const QByteArray&)), + this, SLOT(slotJavaRequest(const QByteArray&)) ); + + setupJava( process ); + + if( process->startJava() ) { + d->appletLabel = i18n( "Loading Applet" ); + d->javaProcessFailed = false; + } + else { + d->appletLabel = i18n( "Error: java executable not found" ); + d->javaProcessFailed = true; + } +} + +KJavaAppletServer::~KJavaAppletServer() +{ + quit(); + + delete process; + delete d; +} + +QString KJavaAppletServer::getAppletLabel() +{ + if( self ) + return self->appletLabel(); + else + return QString::null; +} + +QString KJavaAppletServer::appletLabel() +{ + return d->appletLabel; +} + +KJavaAppletServer* KJavaAppletServer::allocateJavaServer() +{ + if( self == 0 ) + { + self = new KJavaAppletServer(); + self->d->counter = 0; + } + + ++(self->d->counter); + return self; +} + +void KJavaAppletServer::freeJavaServer() +{ + --(self->d->counter); + + if( self->d->counter == 0 ) + { + //instead of immediately quitting here, set a timer to kill us + //if there are still no servers- give us one minute + //this is to prevent repeated loading and unloading of the jvm + KConfig config( "konquerorrc", true ); + config.setGroup( "Java/JavaScript Settings" ); + if( config.readBoolEntry( "ShutdownAppletServer", true ) ) + { + const int value = config.readNumEntry( "AppletServerTimeout", 60 ); + QTimer::singleShot( value*1000, self, SLOT( checkShutdown() ) ); + } + } +} + +void KJavaAppletServer::checkShutdown() +{ + if( self->d->counter == 0 ) + { + delete self; + self = 0; + } +} + +void KJavaAppletServer::setupJava( KJavaProcess *p ) +{ + KConfig config ( "konquerorrc", true ); + config.setGroup( "Java/JavaScript Settings" ); + + QString jvm_path = "java"; + + QString jPath = config.readPathEntry( "JavaPath" ); + if ( !jPath.isEmpty() && jPath != "java" ) + { + // Cut off trailing slash if any + if( jPath[jPath.length()-1] == '/' ) + jPath.remove(jPath.length()-1, 1); + + QDir dir( jPath ); + if( dir.exists( "bin/java" ) ) + { + jvm_path = jPath + "/bin/java"; + } + else if (dir.exists( "/jre/bin/java" ) ) + { + jvm_path = jPath + "/jre/bin/java"; + } + else if( QFile::exists(jPath) ) + { + //check here to see if they entered the whole path the java exe + jvm_path = jPath; + } + } + + //check to see if jvm_path is valid and set d->appletLabel accordingly + p->setJVMPath( jvm_path ); + + // Prepare classpath variable + QString kjava_class = locate("data", "kjava/kjava.jar"); + kdDebug(6100) << "kjava_class = " << kjava_class << endl; + if( kjava_class.isNull() ) // Should not happen + return; + + QDir dir( kjava_class ); + dir.cdUp(); + kdDebug(6100) << "dir = " << dir.absPath() << endl; + + const QStringList entries = dir.entryList( "*.jar" ); + kdDebug(6100) << "entries = " << entries.join( ":" ) << endl; + + QString classes; + { + QStringList::ConstIterator it = entries.begin(); + const QStringList::ConstIterator itEnd = entries.end(); + for( ; it != itEnd; ++it ) + { + if( !classes.isEmpty() ) + classes += ":"; + classes += dir.absFilePath( *it ); + } + } + p->setClasspath( classes ); + + // Fix all the extra arguments + const QString extraArgs = config.readEntry( "JavaArgs" ); + p->setExtraArgs( extraArgs ); + + if( config.readBoolEntry( "UseSecurityManager", true ) ) + { + QString class_file = locate( "data", "kjava/kjava.policy" ); + p->setSystemProperty( "java.security.policy", class_file ); + + p->setSystemProperty( "java.security.manager", + "org.kde.kjas.server.KJASSecurityManager" ); + } + + d->useKIO = config.readBoolEntry( "UseKio", false); + if( d->useKIO ) + { + p->setSystemProperty( "kjas.useKio", QString::null ); + } + + //check for http proxies... + if( KProtocolManager::useProxy() ) + { + // only proxyForURL honors automatic proxy scripts + // we do not know the applet url here so we just use a dummy url + // this is a workaround for now + // FIXME + const KURL dummyURL( "http://www.kde.org/" ); + const QString httpProxy = KProtocolManager::proxyForURL(dummyURL); + kdDebug(6100) << "httpProxy is " << httpProxy << endl; + + const KURL url( httpProxy ); + p->setSystemProperty( "http.proxyHost", url.host() ); + p->setSystemProperty( "http.proxyPort", QString::number( url.port() ) ); + } + + //set the main class to run + p->setMainClass( "org.kde.kjas.server.Main" ); +} + +void KJavaAppletServer::createContext( int contextId, KJavaAppletContext* context ) +{ +// kdDebug(6100) << "createContext: " << contextId << endl; + if ( d->javaProcessFailed ) return; + + d->contexts.insert( contextId, context ); + + QStringList args; + args.append( QString::number( contextId ) ); + process->send( KJAS_CREATE_CONTEXT, args ); +} + +void KJavaAppletServer::destroyContext( int contextId ) +{ +// kdDebug(6100) << "destroyContext: " << contextId << endl; + if ( d->javaProcessFailed ) return; + d->contexts.remove( contextId ); + + QStringList args; + args.append( QString::number( contextId ) ); + process->send( KJAS_DESTROY_CONTEXT, args ); +} + +bool KJavaAppletServer::createApplet( int contextId, int appletId, + const QString & name, const QString & clazzName, + const QString & baseURL, const QString & user, + const QString & password, const QString & authname, + const QString & codeBase, const QString & jarFile, + QSize size, const QMap<QString,QString>& params, + const QString & windowTitle ) +{ +// kdDebug(6100) << "createApplet: contextId = " << contextId << endl +// << " appletId = " << appletId << endl +// << " name = " << name << endl +// << " clazzName = " << clazzName << endl +// << " baseURL = " << baseURL << endl +// << " codeBase = " << codeBase << endl +// << " jarFile = " << jarFile << endl +// << " width = " << size.width() << endl +// << " height = " << size.height() << endl; + + if ( d->javaProcessFailed ) return false; + + QStringList args; + args.append( QString::number( contextId ) ); + args.append( QString::number( appletId ) ); + + //it's ok if these are empty strings, I take care of it later... + args.append( name ); + args.append( clazzName ); + args.append( baseURL ); + args.append( user ); + args.append( password ); + args.append( authname ); + args.append( codeBase ); + args.append( jarFile ); + + args.append( QString::number( size.width() ) ); + args.append( QString::number( size.height() ) ); + + args.append( windowTitle ); + + //add on the number of parameter pairs... + const int num = params.count(); + const QString num_params = QString("%1").arg( num, 8 ); + args.append( num_params ); + + QMap< QString, QString >::ConstIterator it = params.begin(); + const QMap< QString, QString >::ConstIterator itEnd = params.end(); + + for( ; it != itEnd; ++it ) + { + args.append( it.key() ); + args.append( it.data() ); + } + + process->send( KJAS_CREATE_APPLET, args ); + + return true; +} + +void KJavaAppletServer::initApplet( int contextId, int appletId ) +{ + if ( d->javaProcessFailed ) return; + QStringList args; + args.append( QString::number( contextId ) ); + args.append( QString::number( appletId ) ); + + process->send( KJAS_INIT_APPLET, args ); +} + +void KJavaAppletServer::destroyApplet( int contextId, int appletId ) +{ + if ( d->javaProcessFailed ) return; + QStringList args; + args.append( QString::number(contextId) ); + args.append( QString::number(appletId) ); + + process->send( KJAS_DESTROY_APPLET, args ); +} + +void KJavaAppletServer::startApplet( int contextId, int appletId ) +{ + if ( d->javaProcessFailed ) return; + QStringList args; + args.append( QString::number(contextId) ); + args.append( QString::number(appletId) ); + + process->send( KJAS_START_APPLET, args ); +} + +void KJavaAppletServer::stopApplet( int contextId, int appletId ) +{ + if ( d->javaProcessFailed ) return; + QStringList args; + args.append( QString::number(contextId) ); + args.append( QString::number(appletId) ); + + process->send( KJAS_STOP_APPLET, args ); +} + +void KJavaAppletServer::showConsole() { + if ( d->javaProcessFailed ) return; + QStringList args; + process->send( KJAS_SHOW_CONSOLE, args ); +} + +void KJavaAppletServer::sendURLData( int loaderID, int code, const QByteArray& data ) +{ + QStringList args; + args.append( QString::number(loaderID) ); + args.append( QString::number(code) ); + + process->send( KJAS_URLDATA, args, data ); +} + +void KJavaAppletServer::removeDataJob( int loaderID ) +{ + const KIOJobMap::iterator it = d->kiojobs.find( loaderID ); + if (it != d->kiojobs.end()) { + it.data()->deleteLater(); + d->kiojobs.erase( it ); + } +} + +void KJavaAppletServer::quit() +{ + const QStringList args; + + process->send( KJAS_SHUTDOWN_SERVER, args ); + process->flushBuffers(); + process->wait( 10 ); +} + +void KJavaAppletServer::slotJavaRequest( const QByteArray& qb ) +{ + // qb should be one command only without the length string, + // we parse out the command and it's meaning here... + QString cmd; + QStringList args; + int index = 0; + const int qb_size = qb.size(); + + //get the command code + const char cmd_code = qb[ index++ ]; + ++index; //skip the next sep + + //get contextID + QString contextID; + while( qb[index] != 0 && index < qb_size ) + { + contextID += qb[ index++ ]; + } + bool ok; + const int ID_num = contextID.toInt( &ok ); // context id or kio job id + /*if (d->locked_context > -1 && + ID_num != d->locked_context && + (cmd_code == KJAS_JAVASCRIPT_EVENT || + cmd_code == KJAS_APPLET_STATE || + cmd_code == KJAS_APPLET_FAILED)) + { + / * Don't allow requests from other contexts if we're waiting + * on a return value that can trigger JavaScript events + * / + d->java_requests.push_back(qb); + return; + }*/ + ++index; //skip the sep + + if (cmd_code == KJAS_PUT_DATA) { + // rest of the data is for kio put + if (ok) { + KIOJobMap::iterator it = d->kiojobs.find( ID_num ); + if (ok && it != d->kiojobs.end()) { + QByteArray qba; + qba.setRawData(qb.data() + index, qb.size() - index - 1); + it.data()->data(qba); + qba.resetRawData(qb.data() + index, qb.size() - index - 1); + } + kdDebug(6100) << "PutData(" << ID_num << ") size=" << qb.size() - index << endl; + } else + kdError(6100) << "PutData error " << ok << endl; + return; + } + //now parse out the arguments + while( index < qb_size ) + { + int sep_pos = qb.find( 0, index ); + if (sep_pos < 0) { + kdError(6100) << "Missing separation byte" << endl; + sep_pos = qb_size; + } + //kdDebug(6100) << "KJavaAppletServer::slotJavaRequest: "<< QString::fromLocal8Bit( qb.data() + index, sep_pos - index ) << endl; + args.append( QString::fromLocal8Bit( qb.data() + index, sep_pos - index ) ); + index = sep_pos + 1; //skip the sep + } + //here I should find the context and call the method directly + //instead of emitting signals + switch( cmd_code ) + { + case KJAS_SHOW_DOCUMENT: + cmd = QString::fromLatin1( "showdocument" ); + break; + + case KJAS_SHOW_URLINFRAME: + cmd = QString::fromLatin1( "showurlinframe" ); + break; + + case KJAS_SHOW_STATUS: + cmd = QString::fromLatin1( "showstatus" ); + break; + + case KJAS_RESIZE_APPLET: + cmd = QString::fromLatin1( "resizeapplet" ); + break; + + case KJAS_GET_URLDATA: + if (ok && !args.empty() ) { + d->kiojobs.insert(ID_num, new KJavaDownloader(ID_num, args.first())); + kdDebug(6100) << "GetURLData(" << ID_num << ") url=" << args.first() << endl; + } else + kdError(6100) << "GetURLData error " << ok << " args:" << args.size() << endl; + return; + case KJAS_PUT_URLDATA: + if (ok && !args.empty()) { + KJavaUploader* const job = new KJavaUploader(ID_num, args.first()); + d->kiojobs.insert(ID_num, job); + job->start(); + kdDebug(6100) << "PutURLData(" << ID_num << ") url=" << args.first() << endl; + } else + kdError(6100) << "PutURLData error " << ok << " args:" << args.size() << endl; + return; + case KJAS_DATA_COMMAND: + if (ok && !args.empty()) { + const int cmd = args.first().toInt( &ok ); + KIOJobMap::iterator it = d->kiojobs.find( ID_num ); + if (ok && it != d->kiojobs.end()) + it.data()->jobCommand( cmd ); + kdDebug(6100) << "KIO Data command: " << ID_num << " " << args.first() << endl; + } else + kdError(6100) << "KIO Data command error " << ok << " args:" << args.size() << endl; + return; + case KJAS_JAVASCRIPT_EVENT: + cmd = QString::fromLatin1( "JS_Event" ); + kdDebug(6100) << "Javascript request: "<< contextID + << " code: " << args[0] << endl; + break; + case KJAS_GET_MEMBER: + case KJAS_PUT_MEMBER: + case KJAS_CALL_MEMBER: { + const int ticket = args[0].toInt(); + JSStack::iterator it = d->jsstack.find(ticket); + if (it != d->jsstack.end()) { + kdDebug(6100) << "slotJavaRequest: " << ticket << endl; + args.pop_front(); + it.data()->args.operator=(args); // just in case .. + it.data()->ready = true; + it.data()->exit = true; + } else + kdDebug(6100) << "Error: Missed return member data" << endl; + return; + } + case KJAS_AUDIOCLIP_PLAY: + cmd = QString::fromLatin1( "audioclip_play" ); + kdDebug(6100) << "Audio Play: url=" << args[0] << endl; + break; + case KJAS_AUDIOCLIP_LOOP: + cmd = QString::fromLatin1( "audioclip_loop" ); + kdDebug(6100) << "Audio Loop: url=" << args[0] << endl; + break; + case KJAS_AUDIOCLIP_STOP: + cmd = QString::fromLatin1( "audioclip_stop" ); + kdDebug(6100) << "Audio Stop: url=" << args[0] << endl; + break; + case KJAS_APPLET_STATE: + kdDebug(6100) << "Applet State Notification for Applet " << args[0] << ". New state=" << args[1] << endl; + cmd = QString::fromLatin1( "AppletStateNotification" ); + break; + case KJAS_APPLET_FAILED: + kdDebug(6100) << "Applet " << args[0] << " Failed: " << args[1] << endl; + cmd = QString::fromLatin1( "AppletFailed" ); + break; + case KJAS_SECURITY_CONFIRM: { + if (KSSL::doesSSLWork() && !d->kssl) + d->kssl = new KSSL; + QStringList sl; + QCString answer( "invalid" ); + + if (!d->kssl) { + answer = "nossl"; + } else if (args.size() > 2) { + const int certsnr = args[1].toInt(); + QString text; + QPtrList<KSSLCertificate> certs; + certs.setAutoDelete( true ); + for (int i = certsnr; i >= 0; --i) { + KSSLCertificate * cert = KSSLCertificate::fromString(args[i+2].ascii()); + if (cert) { + certs.prepend(cert); + if (cert->isSigner()) + text += i18n("Signed by (validation: "); + else + text += i18n("Certificate (validation: "); + switch (cert->validate()) { + case KSSLCertificate::Ok: + text += i18n("Ok"); break; + case KSSLCertificate::NoCARoot: + text += i18n("NoCARoot"); break; + case KSSLCertificate::InvalidPurpose: + text += i18n("InvalidPurpose"); break; + case KSSLCertificate::PathLengthExceeded: + text += i18n("PathLengthExceeded"); break; + case KSSLCertificate::InvalidCA: + text += i18n("InvalidCA"); break; + case KSSLCertificate::Expired: + text += i18n("Expired"); break; + case KSSLCertificate::SelfSigned: + text += i18n("SelfSigned"); break; + case KSSLCertificate::ErrorReadingRoot: + text += i18n("ErrorReadingRoot"); break; + case KSSLCertificate::Revoked: + text += i18n("Revoked"); break; + case KSSLCertificate::Untrusted: + text += i18n("Untrusted"); break; + case KSSLCertificate::SignatureFailed: + text += i18n("SignatureFailed"); break; + case KSSLCertificate::Rejected: + text += i18n("Rejected"); break; + case KSSLCertificate::PrivateKeyFailed: + text += i18n("PrivateKeyFailed"); break; + case KSSLCertificate::InvalidHost: + text += i18n("InvalidHost"); break; + case KSSLCertificate::Unknown: + default: + text += i18n("Unknown"); break; + } + text += QString(")\n"); + QString subject = cert->getSubject() + QChar('\n'); + QRegExp reg(QString("/[A-Z]+=")); + int pos = 0; + while ((pos = subject.find(reg, pos)) > -1) + subject.replace(pos, 1, QString("\n ")); + text += subject.mid(1); + } + } + kdDebug(6100) << "Security confirm " << args.first() << certs.count() << endl; + if ( !certs.isEmpty() ) { + KSSLCertChain chain; + chain.setChain( certs ); + if ( chain.isValid() ) + answer = PermissionDialog( qApp->activeWindow() ).exec( text, args[0] ); + } + } + sl.push_front( QString(answer) ); + sl.push_front( QString::number(ID_num) ); + process->send( KJAS_SECURITY_CONFIRM, sl ); + return; + } + default: + return; + break; + } + + + if( !ok ) + { + kdError(6100) << "could not parse out contextID to call command on" << endl; + return; + } + + KJavaAppletContext* const context = d->contexts[ ID_num ]; + if( context ) + context->processCmd( cmd, args ); + else if (cmd != "AppletStateNotification") + kdError(6100) << "no context object for this id" << endl; +} + +void KJavaAppletServer::endWaitForReturnData() { + kdDebug(6100) << "KJavaAppletServer::endWaitForReturnData" << endl; + killTimers(); + JSStack::iterator it = d->jsstack.begin(); + JSStack::iterator itEnd = d->jsstack.end(); + for (; it != itEnd; ++it) + it.data()->exit = true; +} + +void KJavaAppletServer::timerEvent(QTimerEvent *) { + endWaitForReturnData(); + kdDebug(6100) << "KJavaAppletServer::timerEvent timeout" << endl; +} + +void KJavaAppletServer::waitForReturnData(JSStackFrame * frame) { + kdDebug(6100) << ">KJavaAppletServer::waitForReturnData" << endl; + killTimers(); + startTimer(15000); + while (!frame->exit) + kapp->eventLoop()->processEvents (QEventLoop::AllEvents | QEventLoop::WaitForMore); + if (d->jsstack.size() <= 1) + killTimers(); + kdDebug(6100) << "<KJavaAppletServer::waitForReturnData stacksize:" << d->jsstack.size() << endl; +} + +bool KJavaAppletServer::getMember(QStringList & args, QStringList & ret_args) { + JSStackFrame frame( d->jsstack, ret_args ); + args.push_front( QString::number(frame.ticket) ); + + process->send( KJAS_GET_MEMBER, args ); + waitForReturnData( &frame ); + + return frame.ready; +} + +bool KJavaAppletServer::putMember( QStringList & args ) { + QStringList ret_args; + JSStackFrame frame( d->jsstack, ret_args ); + args.push_front( QString::number(frame.ticket) ); + + process->send( KJAS_PUT_MEMBER, args ); + waitForReturnData( &frame ); + + return frame.ready && ret_args.count() > 0 && ret_args[0].toInt(); +} + +bool KJavaAppletServer::callMember(QStringList & args, QStringList & ret_args) { + JSStackFrame frame( d->jsstack, ret_args ); + args.push_front( QString::number(frame.ticket) ); + + process->send( KJAS_CALL_MEMBER, args ); + waitForReturnData( &frame ); + + return frame.ready; +} + +void KJavaAppletServer::derefObject( QStringList & args ) { + process->send( KJAS_DEREF_OBJECT, args ); +} + +bool KJavaAppletServer::usingKIO() { + return d->useKIO; +} + + +PermissionDialog::PermissionDialog( QWidget* parent ) + : QObject(parent), m_button("no") +{} + +QCString PermissionDialog::exec( const QString & cert, const QString & perm ) { + QGuardedPtr<QDialog> dialog = new QDialog( static_cast<QWidget*>(parent()), "PermissionDialog"); + + dialog->setSizePolicy( QSizePolicy( (QSizePolicy::SizeType)1, (QSizePolicy::SizeType)1, 0, 0, dialog->sizePolicy().hasHeightForWidth() ) ); + dialog->setModal( true ); + dialog->setCaption( i18n("Security Alert") ); + + QVBoxLayout* const dialogLayout = new QVBoxLayout( dialog, 11, 6, "dialogLayout"); + + dialogLayout->addWidget( new QLabel( i18n("Do you grant Java applet with certificate(s):"), dialog ) ); + dialogLayout->addWidget( new QLabel( cert, dialog, "message" ) ); + dialogLayout->addWidget( new QLabel( i18n("the following permission"), dialog, "message" ) ); + dialogLayout->addWidget( new QLabel( perm, dialog, "message" ) ); + QSpacerItem* const spacer2 = new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding ); + dialogLayout->addItem( spacer2 ); + + QHBoxLayout* const buttonLayout = new QHBoxLayout( 0, 0, 6, "buttonLayout"); + + QPushButton* const no = new QPushButton( i18n("&No"), dialog, "no" ); + no->setDefault( true ); + buttonLayout->addWidget( no ); + + QPushButton* const reject = new QPushButton( i18n("&Reject All"), dialog, "reject" ); + buttonLayout->addWidget( reject ); + + QPushButton* const yes = new QPushButton( i18n("&Yes"), dialog, "yes" ); + buttonLayout->addWidget( yes ); + + QPushButton* const grant = new QPushButton( i18n("&Grant All"), dialog, "grant" ); + buttonLayout->addWidget( grant ); + dialogLayout->addLayout( buttonLayout ); + dialog->resize( dialog->minimumSizeHint() ); + //clearWState( WState_Polished ); + + connect( no, SIGNAL( clicked() ), this, SLOT( clicked() ) ); + connect( reject, SIGNAL( clicked() ), this, SLOT( clicked() ) ); + connect( yes, SIGNAL( clicked() ), this, SLOT( clicked() ) ); + connect( grant, SIGNAL( clicked() ), this, SLOT( clicked() ) ); + + dialog->exec(); + delete dialog; + + return m_button; +} + +PermissionDialog::~PermissionDialog() +{} + +void PermissionDialog::clicked() +{ + m_button = sender()->name(); + static_cast<const QWidget*>(sender())->parentWidget()->close(); +} + +#include "kjavaappletserver.moc" diff --git a/khtml/java/kjavaappletserver.h b/khtml/java/kjavaappletserver.h new file mode 100644 index 000000000..674afb4ad --- /dev/null +++ b/khtml/java/kjavaappletserver.h @@ -0,0 +1,180 @@ +// -*- c++ -*- + +/* This file is part of the KDE project + * + * Copyright (C) 2000 Richard Moore <rich@kde.org> + * 2000 Wynn Wilkes <wynnw@caldera.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KJAVAAPPLETSERVER_H +#define KJAVAAPPLETSERVER_H + +#include "kjavaprocess.h" +#include <qobject.h> +#include <qmap.h> + + +/** + * @short Communicates with a KJAS server to display and control Java applets. + * + * @author Richard J. Moore, rich@kde.org + */ + +class KJavaAppletContext; +class KJavaAppletServerPrivate; +class JSStackFrame; + +class KJavaAppletServer : public QObject +{ +Q_OBJECT + +public: + /** + * Create the applet server. These shouldn't be used directly, + * use allocateJavaServer instead + */ + KJavaAppletServer(); + ~KJavaAppletServer(); + + /** + * A factory method that returns the default server. This is the way this + * class is usually instantiated. + */ + static KJavaAppletServer *allocateJavaServer(); + + /** + * When you are done using your reference to the AppletServer, you must + * dereference it by calling freeJavaServer(). + */ + static void freeJavaServer(); + + /** + * This allows the KJavaAppletWidget to display some feedback in a QLabel + * while the applet is being loaded. If the java process could not be + * started, an error message is displayed instead. + */ + static QString getAppletLabel(); + + /** + * Create an applet context with the specified id. + */ + void createContext( int contextId, KJavaAppletContext* context ); + + /** + * Destroy the applet context with the specified id. All the applets in the + * context will be destroyed as well. + */ + void destroyContext( int contextId ); + + /** + * Create an applet in the specified context with the specified id. The applet + * name, class etc. are specified in the same way as in the HTML APPLET tag. + */ + bool createApplet( int contextId, int appletId, + const QString & name, const QString & clazzName, + const QString & baseURL, const QString & user, + const QString & password, const QString & authname, + const QString & codeBase, const QString & jarFile, + QSize size, const QMap<QString, QString>& params, + const QString & windowTitle ); + + /** + * This should be called by the KJavaAppletWidget + */ + void initApplet( int contextId, int appletId ); + + /** + * Destroy an applet in the specified context with the specified id. + */ + void destroyApplet( int contextId, int appletId ); + + /** + * Start the specified applet. + */ + void startApplet( int contextId, int appletId ); + + /** + * Stop the specified applet. + */ + void stopApplet( int contextId, int appletId ); + + /** + * Show java console. + */ + void showConsole(); + + /** + * Send data we got back from a KJavaDownloader back to the appropriate + * class loader. + */ + void sendURLData( int loaderID, int code, const QByteArray& data ); + /** + * Removes KJavaDownloader from the list (deletes it too). + */ + void removeDataJob( int loaderID ); + + /** + * Shut down the KJAS server. + */ + void quit(); + KJavaProcess* javaProcess() { return process; } + + QString appletLabel(); + + void waitForReturnData(JSStackFrame *); + void endWaitForReturnData(); + + bool getMember(QStringList & args, QStringList & ret_args); + bool putMember(QStringList & args); + bool callMember(QStringList & args, QStringList & ret_args); + void derefObject(QStringList & args); + + bool usingKIO(); +protected: + void setupJava( KJavaProcess* p ); + + KJavaProcess* process; + +protected slots: + void slotJavaRequest( const QByteArray& qb ); + void checkShutdown(); + void timerEvent(QTimerEvent *); + +private: + KJavaAppletServerPrivate* d; + +}; + + +class PermissionDialog : public QObject +{ + Q_OBJECT +public: + PermissionDialog( QWidget* ); + ~PermissionDialog(); + + QCString exec( const QString & cert, const QString & perm ); + +private slots: + void clicked(); + +private: + QCString m_button; +}; + +#endif // KJAVAAPPLETSERVER_H diff --git a/khtml/java/kjavaappletviewer.cpp b/khtml/java/kjavaappletviewer.cpp new file mode 100644 index 000000000..53e8b4512 --- /dev/null +++ b/khtml/java/kjavaappletviewer.cpp @@ -0,0 +1,635 @@ +/* This file is part of the KDE project + * + * Copyright (C) 2003 Koos Vriezen <koos.vriezen@xs4all.nl> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#include <stdio.h> + +#ifdef KDE_USE_FINAL +#undef Always +#endif +#include <qdir.h> +#include <qtable.h> +#include <qpair.h> +#include <qtimer.h> +#include <qguardedptr.h> +#include <qlabel.h> + +#include <klibloader.h> +#include <kaboutdata.h> +#include <kstaticdeleter.h> +#include <klocale.h> +#include <kstatusbar.h> +#include <kiconloader.h> +#include <kapplication.h> +#include <kdebug.h> +#include <kconfig.h> +#include <kio/authinfo.h> +#include <dcopclient.h> + +#include "kjavaappletwidget.h" +#include "kjavaappletviewer.h" +#include "kjavaappletserver.h" + + +K_EXPORT_COMPONENT_FACTORY (kjavaappletviewer, KJavaAppletViewerFactory) + +KInstance *KJavaAppletViewerFactory::s_instance = 0; + +KJavaAppletViewerFactory::KJavaAppletViewerFactory () { + s_instance = new KInstance ("kjava"); +} + +KJavaAppletViewerFactory::~KJavaAppletViewerFactory () { + delete s_instance; +} + +KParts::Part *KJavaAppletViewerFactory::createPartObject + (QWidget *wparent, const char *wname, + QObject *parent, const char * name, const char *, const QStringList & args) { + return new KJavaAppletViewer (wparent, wname, parent, name, args); +} + +//----------------------------------------------------------------------------- + +class KJavaServerMaintainer; +static KJavaServerMaintainer * serverMaintainer = 0; + +class KJavaServerMaintainer { +public: + KJavaServerMaintainer () { } + ~KJavaServerMaintainer (); + + KJavaAppletContext * getContext (QObject*, const QString &); + void releaseContext (QObject*, const QString &); + void setServer (KJavaAppletServer * s); + QGuardedPtr <KJavaAppletServer> server; +private: + typedef QMap <QPair <QObject*, QString>, QPair <KJavaAppletContext*, int> > + ContextMap; + ContextMap m_contextmap; +}; + +KJavaServerMaintainer::~KJavaServerMaintainer () { + delete server; +} + +KJavaAppletContext * KJavaServerMaintainer::getContext (QObject * w, const QString & doc) { + ContextMap::key_type key = qMakePair (w, doc); + ContextMap::iterator it = m_contextmap.find (key); + if (it != m_contextmap.end ()) { + ++((*it).second); + return (*it).first; + } + KJavaAppletContext* const context = new KJavaAppletContext (); + m_contextmap.insert (key, qMakePair(context, 1)); + return context; +} + +void KJavaServerMaintainer::releaseContext (QObject * w, const QString & doc) { + ContextMap::iterator it = m_contextmap.find (qMakePair (w, doc)); + if (it != m_contextmap.end () && --(*it).second <= 0) { + kdDebug(6100) << "KJavaServerMaintainer::releaseContext" << endl; + (*it).first->deleteLater (); + m_contextmap.remove (it); + } +} + +inline void KJavaServerMaintainer::setServer (KJavaAppletServer * s) { + if (!server) + server = s; +} + +static KStaticDeleter <KJavaServerMaintainer> serverMaintainerDeleter; + +//----------------------------------------------------------------------------- + +AppletParameterDialog::AppletParameterDialog (KJavaAppletWidget * parent) + : KDialogBase (parent, "paramdialog", true, i18n ("Applet Parameters"), + KDialogBase::Close, KDialogBase::Close, true), + m_appletWidget (parent) { + KJavaApplet* const applet = parent->applet (); + table = new QTable (30, 2, this); + table->setMinimumSize (QSize (600, 400)); + table->setColumnWidth (0, 200); + table->setColumnWidth (1, 340); + QHeader* const header = table->horizontalHeader(); + header->setLabel (0, i18n ("Parameter")); + header->setLabel (1, i18n ("Value")); + QTableItem * tit = new QTableItem (table, QTableItem::Never, i18n("Class")); + table->setItem (0, 0, tit); + tit = new QTableItem(table, QTableItem::Always, applet->appletClass()); + table->setItem (0, 1, tit); + tit = new QTableItem (table, QTableItem::Never, i18n ("Base URL")); + table->setItem (1, 0, tit); + tit = new QTableItem(table, QTableItem::Always, applet->baseURL()); + table->setItem (1, 1, tit); + tit = new QTableItem (table, QTableItem::Never, i18n ("Archives")); + table->setItem (2, 0, tit); + tit = new QTableItem(table, QTableItem::Always, applet->archives()); + table->setItem (2, 1, tit); + QMap<QString,QString>::const_iterator it = applet->getParams().begin(); + const QMap<QString,QString>::const_iterator itEnd = applet->getParams().end(); + for (int count = 2; it != itEnd; ++it) { + tit = new QTableItem (table, QTableItem::Always, it.key ()); + table->setItem (++count, 0, tit); + tit = new QTableItem(table, QTableItem::Always, it.data ()); + table->setItem (count, 1, tit); + } + setMainWidget (table); +} + +void AppletParameterDialog::slotClose () { + table->selectCells (0, 0, 0, 0); + KJavaApplet* const applet = m_appletWidget->applet (); + applet->setAppletClass (table->item (0, 1)->text ()); + applet->setBaseURL (table->item (1, 1)->text ()); + applet->setArchives (table->item (2, 1)->text ()); + const int lim = table->numRows(); + for (int i = 3; i < lim; ++i) { + if (table->item (i, 0) && table->item (i, 1) && !table->item (i, 0)->text ().isEmpty ()) + applet->setParameter (table->item (i, 0)->text (), + table->item (i, 1)->text ()); + } + hide (); +} +//----------------------------------------------------------------------------- + +class CoverWidget : public QWidget { + KJavaAppletWidget * m_appletwidget; +public: + CoverWidget (QWidget *); + ~CoverWidget () {} + KJavaAppletWidget * appletWidget () const; +protected: + void resizeEvent (QResizeEvent * e); +}; + +inline CoverWidget::CoverWidget (QWidget * parent) + : QWidget (parent, "KJavaAppletViewer Widget") +{ + m_appletwidget = new KJavaAppletWidget (this); + setFocusProxy (m_appletwidget); +} + +inline KJavaAppletWidget * CoverWidget::appletWidget () const { + return m_appletwidget; +} + +void CoverWidget::resizeEvent (QResizeEvent * e) { + m_appletwidget->resize (e->size().width(), e->size().height()); +} + +//----------------------------------------------------------------------------- + +class StatusBarIcon : public QLabel { +public: + StatusBarIcon (QWidget * parent) : QLabel (parent) { + setPixmap (SmallIcon (QString ("java"), KJavaAppletViewerFactory::instance ())); + } +protected: + void mousePressEvent (QMouseEvent *) { + serverMaintainer->server->showConsole (); + } +}; + +//----------------------------------------------------------------------------- + +KJavaAppletViewer::KJavaAppletViewer (QWidget * wparent, const char *, + QObject * parent, const char * name, const QStringList & args) + : KParts::ReadOnlyPart (parent, name), + m_browserextension (new KJavaAppletViewerBrowserExtension (this)), + m_liveconnect (new KJavaAppletViewerLiveConnectExtension (this)), + m_statusbar (new KParts::StatusBarExtension (this)), + m_statusbar_icon (0L), + m_closed (true) +{ + if (!serverMaintainer) { + serverMaintainerDeleter.setObject (serverMaintainer, + new KJavaServerMaintainer); + } + m_view = new CoverWidget (wparent); + QString classname, classid, codebase, khtml_codebase, src_param; + int width = -1; + int height = -1; + KJavaApplet* const applet = m_view->appletWidget()->applet (); + QStringList::const_iterator it = args.begin(); + const QStringList::const_iterator itEnd = args.end(); + for ( ; it != itEnd; ++it) { + const int equalPos = (*it).find("="); + if (equalPos > 0) { + const QString name = (*it).left (equalPos).upper (); + QString value = (*it).right ((*it).length () - equalPos - 1); + if (value.at(0)=='\"') + value = value.right (value.length () - 1); + if (value.at (value.length () - 1) == '\"') + value.truncate (value.length () - 1); + kdDebug(6100) << "name=" << name << " value=" << value << endl; + if (!name.isEmpty()) { + const QString name_lower = name.lower (); + if (name == "__KHTML__PLUGINBASEURL") { + baseurl = KURL (KURL (value), QString (".")).url (); + } else if (name == "__KHTML__CODEBASE") + khtml_codebase = value; + else if (name_lower == QString::fromLatin1("codebase") || + name_lower == QString::fromLatin1("java_codebase")) { + if (!value.isEmpty ()) + codebase = value; + } else if (name == "__KHTML__CLASSID") + //else if (name.lower()==QString::fromLatin1("classid")) + classid = value; + else if (name_lower == QString::fromLatin1("code") || + name_lower == QString::fromLatin1("java_code")) + classname = value; + else if (name_lower == QString::fromLatin1("src")) + src_param = value; + else if (name_lower == QString::fromLatin1("archive") || + name_lower == QString::fromLatin1("java_archive") || + name_lower.startsWith ("cache_archive")) + applet->setArchives (value); + else if (name_lower == QString::fromLatin1("name")) + applet->setAppletName (value); + else if (name_lower == QString::fromLatin1("width")) + width = value.toInt(); + else if (name_lower == QString::fromLatin1("height")) + height = value.toInt(); + if (!name.startsWith ("__KHTML__")) { + applet->setParameter (name, value); + } + } + } + } + if (!classid.isEmpty ()) { + applet->setParameter ("CLSID", classid); + kdDebug(6100) << "classid=" << classid << classid.startsWith("clsid:")<< endl; + if (classid.startsWith ("clsid:")) + // codeBase contains the URL to the plugin page + khtml_codebase = baseurl; + else if (classname.isEmpty () && classid.startsWith ("java:")) + classname = classid.mid(5); + } + if (classname.isEmpty ()) + classname = src_param; + else if (!src_param.isEmpty ()) + applet->setParameter (QString ("SRC"), src_param); + if (codebase.isEmpty ()) + codebase = khtml_codebase; + if (baseurl.isEmpty ()) { + // not embeded in khtml + QString pwd = QDir().absPath (); + if (!pwd.endsWith (QChar (QDir::separator ()))) + pwd += QDir::separator (); + baseurl = KURL (KURL (pwd), codebase).url (); + } + if (width > 0 && height > 0) { + m_view->resize (width, height); + applet->setSize( QSize( width, height ) ); + } + applet->setBaseURL (baseurl); + // check codebase first + const KURL kbaseURL( baseurl ); + const KURL newURL(kbaseURL, codebase); + if (kapp->authorizeURLAction("redirect", KURL(baseurl), newURL)) + applet->setCodeBase (newURL.url()); + applet->setAppletClass (classname); + KJavaAppletContext* const cxt = serverMaintainer->getContext (parent, baseurl); + applet->setAppletContext (cxt); + + KJavaAppletServer* const server = cxt->getServer (); + + serverMaintainer->setServer (server); + + if (!server->usingKIO ()) { + /* if this page needs authentication */ + KIO::AuthInfo info; + QString errorMsg; + QCString replyType; + QByteArray params; + QByteArray reply; + KIO::AuthInfo authResult; + + //(void) dcopClient(); // Make sure to have a dcop client. + info.url = baseurl; + info.verifyPath = true; + + QDataStream stream(params, IO_WriteOnly); + stream << info << m_view->topLevelWidget()->winId(); + + if (!kapp->dcopClient ()->call( "kded", "kpasswdserver", "checkAuthInfo(KIO::AuthInfo, long int)", params, replyType, reply ) ) { + kdWarning() << "Can't communicate with kded_kpasswdserver!" << endl; + } else if ( replyType == "KIO::AuthInfo" ) { + QDataStream stream2( reply, IO_ReadOnly ); + stream2 >> authResult; + applet->setUser (authResult.username); + applet->setPassword (authResult.password); + applet->setAuthName (authResult.realmValue); + } + } + + /* install event filter for close events */ + if (wparent) + wparent->topLevelWidget ()->installEventFilter (this); + + setInstance (KJavaAppletViewerFactory::instance ()); + KParts::Part::setWidget (m_view); + + connect (applet->getContext(), SIGNAL(appletLoaded()), this, SLOT(appletLoaded())); + connect (applet->getContext(), SIGNAL(showDocument(const QString&, const QString&)), m_browserextension, SLOT(showDocument(const QString&, const QString&))); + connect (applet->getContext(), SIGNAL(showStatus(const QString &)), this, SLOT(infoMessage(const QString &))); + connect (applet, SIGNAL(jsEvent (const QStringList &)), m_liveconnect, SLOT(jsEvent (const QStringList &))); +} + +bool KJavaAppletViewer::eventFilter (QObject *o, QEvent *e) { + if (m_liveconnect->jsSessions () > 0) { + switch (e->type()) { + case QEvent::Destroy: + case QEvent::Close: + case QEvent::Quit: + return true; + default: + break; + } + } + return KParts::ReadOnlyPart::eventFilter(o,e); +} + +KJavaAppletViewer::~KJavaAppletViewer () { + m_view = 0L; + serverMaintainer->releaseContext (parent(), baseurl); + if (m_statusbar_icon) { + m_statusbar->removeStatusBarItem (m_statusbar_icon); + delete m_statusbar_icon; + } +} + +bool KJavaAppletViewer::openURL (const KURL & url) { + if (!m_view) return false; + m_closed = false; + KJavaAppletWidget* const w = m_view->appletWidget (); + KJavaApplet* const applet = w->applet (); + if (applet->isCreated ()) + applet->stop (); + if (applet->appletClass ().isEmpty ()) { + // preview without setting a class? + if (applet->baseURL ().isEmpty ()) { + applet->setAppletClass (url.fileName ()); + applet->setBaseURL (url.upURL ().url ()); + } else + applet->setAppletClass (url.url ()); + AppletParameterDialog (w).exec (); + applet->setSize (w->sizeHint()); + } + if (!m_statusbar_icon) { + KStatusBar *sb = m_statusbar->statusBar(); + if (sb) { + m_statusbar_icon = new StatusBarIcon (sb); + m_statusbar->addStatusBarItem (m_statusbar_icon, 0, false); + } + } + // delay showApplet if size is unknown and m_view not shown + if (applet->size().width() > 0 || m_view->isVisible()) + w->showApplet (); + else + QTimer::singleShot (10, this, SLOT (delayedCreateTimeOut ())); + if (!applet->failed ()) + emit started (0L); + return url.isValid (); +} + +bool KJavaAppletViewer::closeURL () { + kdDebug(6100) << "closeURL" << endl; + m_closed = true; + KJavaApplet* const applet = m_view->appletWidget ()->applet (); + if (applet->isCreated ()) + applet->stop (); + applet->getContext()->getServer()->endWaitForReturnData(); + return true; +} + +bool KJavaAppletViewer::appletAlive () const { + return !m_closed && m_view && + m_view->appletWidget ()->applet () && + m_view->appletWidget ()->applet ()->isAlive (); +} + +bool KJavaAppletViewer::openFile () { + return false; +} + +void KJavaAppletViewer::delayedCreateTimeOut () { + KJavaAppletWidget* const w = m_view->appletWidget (); + if (!w->applet ()->isCreated () && !m_closed) + w->showApplet (); +} + +void KJavaAppletViewer::appletLoaded () { + if (!m_view) return; + KJavaApplet* const applet = m_view->appletWidget ()->applet (); + if (applet->isAlive() || applet->failed()) + emit completed(); +} + +void KJavaAppletViewer::infoMessage (const QString & msg) { + m_browserextension->infoMessage(msg); +} + +KAboutData* KJavaAppletViewer::createAboutData () { + return new KAboutData("KJavaAppletViewer", I18N_NOOP("KDE Java Applet Plugin"), "1.0"); +} + +//--------------------------------------------------------------------- + +KJavaAppletViewerBrowserExtension::KJavaAppletViewerBrowserExtension (KJavaAppletViewer * parent) + : KParts::BrowserExtension (parent, "KJavaAppletViewer Browser Extension") { +} + +void KJavaAppletViewerBrowserExtension::urlChanged (const QString & url) { + emit setLocationBarURL (url); +} + +void KJavaAppletViewerBrowserExtension::setLoadingProgress (int percentage) { + emit loadingProgress (percentage); +} + +void KJavaAppletViewerBrowserExtension::setURLArgs (const KParts::URLArgs & /*args*/) { +} + +void KJavaAppletViewerBrowserExtension::saveState (QDataStream & stream) { + KJavaApplet* const applet = static_cast<KJavaAppletViewer*>(parent())->view()->appletWidget ()->applet (); + stream << applet->appletClass(); + stream << applet->baseURL(); + stream << applet->archives(); + stream << applet->getParams().size (); + QMap<QString,QString>::const_iterator it = applet->getParams().begin(); + const QMap<QString,QString>::const_iterator itEnd = applet->getParams().end(); + for ( ; it != itEnd; ++it) { + stream << it.key (); + stream << it.data (); + } +} + +void KJavaAppletViewerBrowserExtension::restoreState (QDataStream & stream) { + KJavaAppletWidget* const w = static_cast<KJavaAppletViewer*>(parent())->view()->appletWidget(); + KJavaApplet* const applet = w->applet (); + QString key, val; + int paramcount; + stream >> val; + applet->setAppletClass (val); + stream >> val; + applet->setBaseURL (val); + stream >> val; + applet->setArchives (val); + stream >> paramcount; + for (int i = 0; i < paramcount; ++i) { + stream >> key; + stream >> val; + applet->setParameter (key, val); + kdDebug(6100) << "restoreState key:" << key << " val:" << val << endl; + } + applet->setSize (w->sizeHint ()); + if (w->isVisible()) + w->showApplet (); +} + +void KJavaAppletViewerBrowserExtension::showDocument (const QString & doc, + const QString & frame) { + const KURL url (doc); + KParts::URLArgs args; + args.frameName = frame; + emit openURLRequest (url, args); +} + +//----------------------------------------------------------------------------- + +KJavaAppletViewerLiveConnectExtension::KJavaAppletViewerLiveConnectExtension(KJavaAppletViewer * parent) + : KParts::LiveConnectExtension (parent, "KJavaAppletViewer LiveConnect Extension"), m_viewer (parent) { +} + +bool KJavaAppletViewerLiveConnectExtension::get ( + const unsigned long objid, const QString & name, + KParts::LiveConnectExtension::Type & type, + unsigned long & rid, QString & value) +{ + if (!m_viewer->appletAlive ()) + return false; + QStringList args, ret_args; + KJavaApplet* const applet = m_viewer->view ()->appletWidget ()->applet (); + args.append (QString::number (applet->appletId ())); + args.append (QString::number ((int) objid)); + args.append (name); + m_jssessions++; + const bool ret = applet->getContext()->getMember (args, ret_args); + m_jssessions--; + if (!ret || ret_args.count() != 3) return false; + bool ok; + int itype = ret_args[0].toInt (&ok); + if (!ok || itype < 0) return false; + type = (KParts::LiveConnectExtension::Type) itype; + rid = ret_args[1].toInt (&ok); + if (!ok) return false; + value = ret_args[2]; + return true; +} + +bool KJavaAppletViewerLiveConnectExtension::put(const unsigned long objid, const QString & name, const QString & value) +{ + if (!m_viewer->appletAlive ()) + return false; + QStringList args; + KJavaApplet* const applet = m_viewer->view ()->appletWidget ()->applet (); + args.append (QString::number (applet->appletId ())); + args.append (QString::number ((int) objid)); + args.append (name); + args.append (value); + ++m_jssessions; + const bool ret = applet->getContext()->putMember (args); + --m_jssessions; + return ret; +} + +bool KJavaAppletViewerLiveConnectExtension::call( const unsigned long objid, const QString & func, const QStringList & fargs, KParts::LiveConnectExtension::Type & type, unsigned long & retobjid, QString & value ) +{ + if (!m_viewer->appletAlive ()) + return false; + KJavaApplet* const applet = m_viewer->view ()->appletWidget ()->applet (); + QStringList args, ret_args; + args.append (QString::number (applet->appletId ())); + args.append (QString::number ((int) objid)); + args.append (func); + args.append (QString::number ((int) fargs.size ())); + { + QStringList::const_iterator it = fargs.begin(); + const QStringList::const_iterator itEnd = fargs.end(); + for ( ; it != itEnd; ++it) + args.append(*it); + } + + ++m_jssessions; + const bool ret = applet->getContext()->callMember (args, ret_args); + --m_jssessions; + if (!ret || ret_args.count () != 3) return false; + bool ok; + const int itype = ret_args[0].toInt (&ok); + if (!ok || itype < 0) return false; + type = (KParts::LiveConnectExtension::Type) itype; + retobjid = ret_args[1].toInt (&ok); + if (!ok) return false; + value = ret_args[2]; + return true; +} + +void KJavaAppletViewerLiveConnectExtension::unregister(const unsigned long objid) +{ + if (!m_viewer->view () || !m_viewer->view ()) + return; + KJavaApplet* const applet = m_viewer->view ()->appletWidget ()->applet (); + if (!applet || objid == 0) { + // typically a gc after a function call on the applet, + // no need to send to the jvm + return; + } + QStringList args; + args.append (QString::number (applet->appletId ())); + args.append (QString::number ((int) objid)); + applet->getContext()->derefObject (args); +} + +void KJavaAppletViewerLiveConnectExtension::jsEvent (const QStringList & args) { + if (args.count () < 2 || !m_viewer->appletAlive ()) + return; + bool ok; + QStringList::ConstIterator it = args.begin(); + const QStringList::ConstIterator itEnd = args.end(); + const unsigned long objid = (*it).toInt(&ok); + ++it; + const QString event = (*it); + ++it; + KParts::LiveConnectExtension::ArgList arglist; + + for (; it != itEnd; ++it) { + // take a deep breath here + const QStringList::ConstIterator prev = it++; + arglist.push_back(KParts::LiveConnectExtension::ArgList::value_type((KParts::LiveConnectExtension::Type) (*prev).toInt(), (*it))); + } + emit partEvent (objid, event, arglist); +} + +int KJavaAppletViewerLiveConnectExtension::m_jssessions = 0; + +//----------------------------------------------------------------------------- + +#include "kjavaappletviewer.moc" diff --git a/khtml/java/kjavaappletviewer.desktop b/khtml/java/kjavaappletviewer.desktop new file mode 100644 index 000000000..ba412cccf --- /dev/null +++ b/khtml/java/kjavaappletviewer.desktop @@ -0,0 +1,82 @@ +[Desktop Entry] +Name=Embedded Java Applet Viewer +Name[af]=Ingelegde Java Program Bekyker +Name[az]=Daxili Java Applet Nümayişçisi +Name[be]=Унутраны праглядальнік аплетаў Java +Name[bg]=Вграден визуализатор на Java аплети +Name[bn]=অভ্যন্তরীণ জাভা অ্যাপলেট প্রদর্শক +Name[br]=Lenner enframmet eus arloadigoù Java +Name[ca]=Applet encastable del visor Java +Name[cs]=Zabudovaný prohlížeč Java appletů +Name[csb]=Wbùdowóny przezérnik apletów Java +Name[cy]=Gwelydd Rhaglennig Java Mewnadeiladedig +Name[da]=Indlejret Java-applet fremviser +Name[de]=Eingebetteter Betrachter für Java-Programme +Name[el]=Ενσωματωμένος προβολέας μικροεφαρμογών Java +Name[eo]=Enkonstruita javaplikaĵrigardilo +Name[es]=Visor de applets de Java empotrables +Name[et]=Põimitav Java apleti näitaja +Name[eu]=Kapsulatutako Javaren applet-ikustailea +Name[fa]=مشاهدهگر برنامک جاوای نهفته +Name[fi]=Upotettava Java-sovelmien näyttäjä +Name[fr]=Afficheur d'applets Java intégré +Name[fy]=yn sluten Java Applet Werjefte +Name[ga]=Amharcán Feidhmchláiríní Java Inleabaithe +Name[gl]=Visualizador de Applets de Java Incrustado +Name[he]=מציג יישומוני Java מוטבע +Name[hi]=अंतर्निहित जावा ऐपलेट दर्शक +Name[hr]=Ugrađeni preglednik Java appleta +Name[hu]=Beágyazott megjelenítőprogram Java kisalkalmazásokhoz +Name[id]=Penampil Applet Java Tersisipkan +Name[is]=Ívefjanlegur Java Applet skoðari +Name[it]=Visualizzatore integrato di applet Java +Name[ja]=埋め込み Java アプレットビューア +Name[ka]=ჩადგმული კომპონენტი Java აპლეტების საჩვენებლად +Name[kk]=Ендірілетін Java апплеттерді қарау модулі +Name[km]=កម្មវិធីមើលអាប់ភ្លេត Java ដែលបានបង្កប់ +Name[ko]=끼워넣은 자바 애플릿 보기 +Name[lb]=Agebetteten Java-Applet-Uweiser +Name[lt]=Įdedamas Java Applet žiūriklis +Name[lv]=Iegultais Java sīklietotņu (apletu) skatītājs +Name[mk]=Вгнезден гледач на Java аплети +Name[mn]=Суулгаж болох Жава Апплет Харагч +Name[ms]=Pelihat Aplet Java Terserta +Name[nb]=Innebygd Java Applet-viser +Name[nds]=Inbett Kieker för Java-Lüttprogrammen +Name[ne]=सम्मिलित जाभा एप्लेट दर्शक +Name[nl]=Ingebed Java Applet-weergaveprogramma +Name[nn]=Innebygd Java-appletvisar +Name[pa]=ਸ਼ਾਮਿਲ Java Applet ਦਰਸ਼ਕ +Name[pl]=Wbudowana przeglądarka apletów Javy +Name[pt]=Visualizador Embebido de 'Applets' Java +Name[pt_BR]=Visualizador Embutido de Applets Java +Name[ro]=Vizualizor înglobat de miniaplicaţii Java +Name[ru]=Встраиваемый модуль просмотра аплетов Java +Name[rw]=Mugaragaza Java Apuleti Irimo +Name[se]=Vuojuhanláhkái Javaprográmmaš čájeheaddji +Name[sk]=Vložiteľný prehliadač Java appletov +Name[sl]=Vgrajeni pregledovalnik vstavkov java +Name[sq]=Shikues i Ngulitur për JAVA Apletat +Name[sr]=Уграђени приказивач Java аплета +Name[sr@Latn]=Ugrađeni prikazivač Java apleta +Name[sv]=Inbäddningsbar visare av Javaminiprogram +Name[ta]=உட்பொதிந்த ஜாவா சிறுநிரல் காட்டி +Name[te]=పొదిగిన జావా ఎపలెట్ చూపరి +Name[tg]=Шоҳиди Java Applet дар Дарун Сохтан +Name[th]=ตัวแสดงจาวาแอพเพล็ตที่ฝังอยู่ +Name[tr]=Gömülü Java Aplet Görüntüleyici +Name[tt]=Java Applet öçen Quşılma-Kürsätkeç +Name[uk]=Вмонтований переглядач аплетів Java +Name[uz]=Ichki Java appletini koʻruvchi +Name[uz@cyrillic]=Ички Java апплетини кўрувчи +Name[vi]=Bộ xem tiểu dụng Java nhúng +Name[wa]=Ravalé håyneu d' apliketes Java +Name[zh_CN]=嵌入的 Java 小程序查看器 +Name[zh_TW]=Embedded Java Applet 檢視器 +X-KDE-Library=kjavaappletviewer +Icon=java +MimeType=application/x-java-applet;application/x-java; +ServiceTypes=KParts/ReadOnlyPart,Browser/View +Type=Service +InitialPreference=2 +X-KDE-BrowserView-PluginsInfo=kjava/pluginsinfo diff --git a/khtml/java/kjavaappletviewer.h b/khtml/java/kjavaappletviewer.h new file mode 100644 index 000000000..4d8b676cd --- /dev/null +++ b/khtml/java/kjavaappletviewer.h @@ -0,0 +1,145 @@ +// -*- c++ -*- + +/* This file is part of the KDE project + * + * Copyright (C) 2003 Koos Vriezen <koos.vriezen@xs4all.nl> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KJAVAAPPLETVIEWER_H +#define KJAVAAPPLETVIEWER_H + +#include <kparts/part.h> +#include <kparts/browserextension.h> +#include <kparts/statusbarextension.h> +#include <kparts/factory.h> +#include <kdialogbase.h> +#include <kurl.h> +#include <qobject.h> +#include <qstringlist.h> +#include <qguardedptr.h> + +#include "kjavaappletwidget.h" + +class QTable; +class QLabel; +class KJavaProcess; +class KJavaAppletViewer; +class KAboutData; +class KInstance; +class KConfig; +class CoverWidget; + +class KJavaAppletViewerBrowserExtension : public KParts::BrowserExtension { + Q_OBJECT +public: + KJavaAppletViewerBrowserExtension (KJavaAppletViewer *parent); + void urlChanged (const QString & url); + void setLoadingProgress (int percentage); + + void setURLArgs (const KParts::URLArgs & args); + void saveState (QDataStream & stream); + void restoreState (QDataStream & stream); +public slots: + void showDocument (const QString & doc, const QString & frame); +}; + +class KJavaAppletViewerLiveConnectExtension : public KParts::LiveConnectExtension { + Q_OBJECT +public: + KJavaAppletViewerLiveConnectExtension(KJavaAppletViewer * parent); + + bool get (const unsigned long objid, const QString & field, KParts::LiveConnectExtension::Type & type, unsigned long & retobjid, QString & value); + bool put(const unsigned long, const QString & field, const QString & value); + bool call (const unsigned long , const QString & func, const QStringList & args, KParts::LiveConnectExtension::Type & type, unsigned long & retobjid, QString & value); + void unregister (const unsigned long objid); + + int jsSessions () const { return m_jssessions; } +public slots: + void jsEvent (const QStringList & args); +signals: + virtual void partEvent (const unsigned long objid, const QString & event, const KParts::LiveConnectExtension::ArgList & args); + +private: + KJavaAppletViewer * m_viewer; + static int m_jssessions; +}; + +class KJavaAppletViewer : public KParts::ReadOnlyPart { + Q_OBJECT +public: + KJavaAppletViewer (QWidget * wparent, const char * wname, + QObject * parent, const char * name, const QStringList &args); + ~KJavaAppletViewer (); + CoverWidget * view () const { return m_view; } + static KAboutData* createAboutData (); + + KJavaAppletViewerBrowserExtension * browserextension() const + { return m_browserextension; } + KParts::LiveConnectExtension * liveConnectExtension () const + { return m_liveconnect; } + + bool eventFilter (QObject *o, QEvent *e); + + bool appletAlive () const; +public slots: + virtual bool openURL (const KURL & url); + virtual bool closeURL (); + void appletLoaded (); + void infoMessage (const QString &); +protected: + bool openFile(); +private slots: + void delayedCreateTimeOut (); +private: + QGuardedPtr <CoverWidget> m_view; + KConfig * m_config; + KJavaProcess * process; + KJavaAppletViewerBrowserExtension * m_browserextension; + KJavaAppletViewerLiveConnectExtension * m_liveconnect; + KParts::StatusBarExtension * m_statusbar; + QGuardedPtr <QLabel> m_statusbar_icon; + QString baseurl; + bool m_closed; +}; + +class KJavaAppletViewerFactory : public KParts::Factory { + Q_OBJECT +public: + KJavaAppletViewerFactory (); + virtual ~KJavaAppletViewerFactory (); + virtual KParts::Part *createPartObject + (QWidget *wparent, const char *wname, + QObject *parent, const char *name, + const char *className, const QStringList &args); + static KInstance * instance () { return s_instance; } +private: + static KInstance * s_instance; +}; + +class AppletParameterDialog : public KDialogBase { + Q_OBJECT +public: + AppletParameterDialog (KJavaAppletWidget * parent); +protected slots: + void slotClose (); +private: + KJavaAppletWidget * m_appletWidget; + QTable * table; +}; + +#endif diff --git a/khtml/java/kjavaappletwidget.cpp b/khtml/java/kjavaappletwidget.cpp new file mode 100644 index 000000000..31ce8b5e6 --- /dev/null +++ b/khtml/java/kjavaappletwidget.cpp @@ -0,0 +1,140 @@ +/* This file is part of the KDE project + * + * Copyright (C) 2000 Richard Moore <rich@kde.org> + * 2000 Wynn Wilkes <wynnw@caldera.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kjavaappletwidget.h" +#include "kjavaappletserver.h" + +#include <kwin.h> +#include <kdebug.h> +#include <klocale.h> + +#include <qlabel.h> + + +// For future expansion +class KJavaAppletWidgetPrivate +{ +friend class KJavaAppletWidget; +private: + QLabel* tmplabel; +}; + +int KJavaAppletWidget::appletCount = 0; + +KJavaAppletWidget::KJavaAppletWidget( QWidget* parent, const char* name ) + : QXEmbed ( parent, name) +{ + setProtocol(QXEmbed::XPLAIN); + + m_applet = new KJavaApplet( this ); + d = new KJavaAppletWidgetPrivate; + m_kwm = new KWinModule( this ); + + d->tmplabel = new QLabel( this ); + d->tmplabel->setText( KJavaAppletServer::getAppletLabel() ); + d->tmplabel->setAlignment( Qt::AlignCenter | Qt::WordBreak ); + d->tmplabel->setFrameStyle( QFrame::StyledPanel | QFrame::Sunken ); + d->tmplabel->show(); + + m_swallowTitle.sprintf( "KJAS Applet - Ticket number %u", appletCount++ ); + m_applet->setWindowName( m_swallowTitle ); +} + +KJavaAppletWidget::~KJavaAppletWidget() +{ + delete m_applet; + delete d; +} + +void KJavaAppletWidget::showApplet() +{ + connect( m_kwm, SIGNAL( windowAdded( WId ) ), + this, SLOT( setWindow( WId ) ) ); + + m_kwm->doNotManage( m_swallowTitle ); + + //Now we send applet info to the applet server + if ( !m_applet->isCreated() ) + m_applet->create(); +} + +void KJavaAppletWidget::setWindow( WId w ) +{ + //make sure that this window has the right name, if so, embed it... + KWin::WindowInfo w_info = KWin::windowInfo( w ); + if ( m_swallowTitle == w_info.name() || + m_swallowTitle == w_info.visibleName() ) + { + kdDebug(6100) << "swallowing our window: " << m_swallowTitle + << ", window id = " << w << endl; + delete d->tmplabel; + d->tmplabel = 0; + + // disconnect from KWM events + disconnect( m_kwm, SIGNAL( windowAdded( WId ) ), + this, SLOT( setWindow( WId ) ) ); + + + embed( w ); + setFocus(); + } +} + +QSize KJavaAppletWidget::sizeHint() const +{ + kdDebug(6100) << "KJavaAppletWidget::sizeHint()" << endl; + QSize rval = QXEmbed::sizeHint(); + + if( rval.width() == 0 || rval.height() == 0 ) + { + if( width() != 0 && height() != 0 ) + { + rval = QSize( width(), height() ); + } + } + + kdDebug(6100) << "returning: (" << rval.width() << ", " << rval.height() << ")" << endl; + + return rval; +} + +void KJavaAppletWidget::resize( int w, int h ) +{ + if( d->tmplabel ) + { + d->tmplabel->resize( w, h ); + m_applet->setSize( QSize( w, h ) ); + } + + QXEmbed::resize( w, h ); +} + +void KJavaAppletWidget::showEvent (QShowEvent * e) { + QXEmbed::showEvent(e); + if (!applet()->isCreated() && !applet()->appletClass().isEmpty()) { + // delayed showApplet + if (applet()->size().width() <= 0) + applet()->setSize (sizeHint()); + showApplet(); + } +} + +#include "kjavaappletwidget.moc" diff --git a/khtml/java/kjavaappletwidget.h b/khtml/java/kjavaappletwidget.h new file mode 100644 index 000000000..24973c648 --- /dev/null +++ b/khtml/java/kjavaappletwidget.h @@ -0,0 +1,125 @@ +// -*- c++ -*- + +/* This file is part of the KDE project + * + * Copyright (C) 2000 Richard Moore <rich@kde.org> + * 2000 Wynn Wilkes <wynnw@caldera.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KJAVAAPPLETWIDGET_H +#define KJAVAAPPLETWIDGET_H + +#include <qwidget.h> +#ifndef Q_WS_QWS //FIXME(?) I don't think this is possible with Qt Embedded +#include "java/kjavaappletcontext.h" +#include "java/kjavaapplet.h" +#include <qxembed.h> +#include <kwinmodule.h> + +/** + * @short A widget for displaying Java applets + * + * KJavaAppletWidget provides support for the inclusion of Java applets + * in Qt and KDE applications. To create an applet, you must first create + * a context object in which it will run. There can be several applets and + * contexts in operation at a given time, for example in a web browser there + * would be one context object for each web page. Applets in the same context + * can communicate with each other, applets in different contexts cannot. + * Once you have created a KJavaAppletContext, you can create as many + * applets in it as you want. + * + * Once you have created the applet widget, you should access the applet() method + * to call the various setXXX methods to configure the applet, + * They correspond to the HTML tags used to embed applets in a web page. + * Once the applet is configured, call the create() method to set things in motion. + * The applet is running when it first appears, but you can start or stop it + * when you like (for example if it scrolls off the screen). + * + * This widget works by using the KJavaAppletServer, which fires off a + * Java server process with which it communicates using the + * KDE Java Applet Server (KJAS) protocol over stdin and stdout. + * The applet windows are swallowed and attached to the QWidget, but they are + * actually running in a different process. This has the advantage of robustness + * and reusability. The details of the communication are hidden from the user + * in the KJASAppletServer class. Normally only a single server process is used for + * all of the applets in a given application, this is all sorted automatically. + * The KJAS server is 100% pure Java, and should also prove useful for people + * wishing to add java support to other systems (for example a perl/Tk binding + * is perfectly feasible). All you need to do is implement the protocol and + * (optionally) swallow the applet windows. + * + * The applet support in KDE is still dependent on the KWin Window Manager. The + * applet swallowing will not work under other Window Managers. Hopefully this + * will be fixed in the future. + * + * For a description of the KJAS protocol, please see the KJAS_GRAMMAR.txt file. + * + * @author Richard J. Moore, rich@kde.org + * @author Wynn Wilkes, wynnw@caldera.com + */ + +class KJavaAppletWidgetPrivate; + +class KJavaAppletWidget : public QXEmbed +{ + Q_OBJECT +public: + KJavaAppletWidget( QWidget* parent=0, const char* name=0 ); + + ~KJavaAppletWidget(); + + /** + * Returns a pointer to the KJavaApplet. Use this to + * configure the applet's parameters. You can also + * use it to start and stop the Applet. + */ + KJavaApplet* applet() { return m_applet; } + + /** + * Tells the AppletServer to create, initialize, and + * show the Applet. + */ + void showApplet(); + + QSize sizeHint() const; + void resize( int, int ); + +protected slots: + /** + * This slot is called by KWin when new windows are added. We check + * to see if the window has the title we set. If so we embed it. + */ + void setWindow( WId w ); + +protected: + //The counter to generate ID's for the applets + static int appletCount; + void showEvent (QShowEvent *); + +private: + KJavaAppletWidgetPrivate* d; + + KJavaApplet* m_applet; + KWinModule* m_kwm; + QString m_swallowTitle; + +}; + +#endif +#endif // KJAVAAPPLETWIDGET_H + diff --git a/khtml/java/kjavadownloader.cpp b/khtml/java/kjavadownloader.cpp new file mode 100644 index 000000000..8241fdea5 --- /dev/null +++ b/khtml/java/kjavadownloader.cpp @@ -0,0 +1,298 @@ +/* This file is part of the KDE project + * + * Copyright (C) 2000 Richard Moore <rich@kde.org> + * 2000 Wynn Wilkes <wynnw@caldera.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kjavadownloader.h" +#include "kjavaappletserver.h" + +#include <kurl.h> +#include <kio/job.h> +#include <kio/jobclasses.h> +#include <kdebug.h> +#include <qfile.h> + +static const int DATA = 0; +static const int FINISHED = 1; +static const int ERRORCODE = 2; +static const int HEADERS = 3; +static const int REDIRECT = 4; +static const int MIMETYPE = 5; +static const int CONNECTED = 6; +static const int REQUESTDATA = 7; + +static const int KJAS_STOP = 0; +static const int KJAS_HOLD = 1; +static const int KJAS_RESUME = 2; + +KJavaKIOJob::~KJavaKIOJob() {} + +void KJavaKIOJob::data( const QByteArray& ) +{ + kdError(6100) << "Job id mixup" << endl; +} + +//----------------------------------------------------------------------------- + +class KJavaDownloaderPrivate +{ +friend class KJavaDownloader; +public: + KJavaDownloaderPrivate() : responseCode(0), isfirstdata(true) {} + ~KJavaDownloaderPrivate() + { + delete url; + if (job) job->kill(); // KIO::Job::kill deletes itself + } +private: + int loaderID; + KURL* url; + QByteArray file; + KIO::TransferJob* job; + int responseCode; + bool isfirstdata; +}; + + +KJavaDownloader::KJavaDownloader( int ID, const QString& url ) +{ + kdDebug(6100) << "KJavaDownloader(" << ID << ") = " << url << endl; + + d = new KJavaDownloaderPrivate; + + d->loaderID = ID; + d->url = new KURL( url ); + + d->job = KIO::get( *d->url, false, false ); + d->job->addMetaData("PropagateHttpHeader", "true"); + connect( d->job, SIGNAL(data( KIO::Job*, const QByteArray& )), + this, SLOT(slotData( KIO::Job*, const QByteArray& )) ); + connect( d->job, SIGNAL(connected(KIO::Job*)), + this, SLOT(slotConnected(KIO::Job*))); + connect( d->job, SIGNAL(mimetype(KIO::Job*, const QString&)), + this, SLOT(slotMimetype(KIO::Job*, const QString&))); + connect( d->job, SIGNAL(result(KIO::Job*)), + this, SLOT(slotResult(KIO::Job*)) ); +} + +KJavaDownloader::~KJavaDownloader() +{ + delete d; +} + +void KJavaDownloader::slotData( KIO::Job*, const QByteArray& qb ) +{ + //kdDebug(6100) << "slotData(" << d->loaderID << ")" << endl; + + KJavaAppletServer* server = KJavaAppletServer::allocateJavaServer(); + if (d->isfirstdata) { + QString headers = d->job->queryMetaData("HTTP-Headers"); + if (!headers.isEmpty()) { + d->file.resize( headers.length() ); + memcpy( d->file.data(), headers.ascii(), headers.length() ); + server->sendURLData( d->loaderID, HEADERS, d->file ); + d->file.resize( 0 ); + } + d->isfirstdata = false; + } + if ( qb.size() ) + server->sendURLData( d->loaderID, DATA, qb ); + KJavaAppletServer::freeJavaServer(); +} + +void KJavaDownloader::slotConnected(KIO::Job*) +{ + kdDebug(6100) << "slave connected" << endl; + d->responseCode = d->job->error(); +} + +void KJavaDownloader::slotMimetype(KIO::Job*, const QString & type) { + kdDebug(6100) << "slave mimetype " << type << endl; +} + +void KJavaDownloader::slotResult( KIO::Job* ) +{ + kdDebug(6100) << "slotResult(" << d->loaderID << ")" << endl; + + KJavaAppletServer* server = KJavaAppletServer::allocateJavaServer(); + if( d->job->error()) + { + kdDebug(6100) << "slave had an error = " << d->job->errorString() << endl; + int code = d->job->error(); + if (!code) + code = 404; + QString codestr = QString::number(code); + d->file.resize(codestr.length()); + memcpy( d->file.data(), codestr.ascii(), codestr.length() ); + kdDebug(6100) << "slave had an error = " << code << endl; + + server->sendURLData( d->loaderID, ERRORCODE, d->file ); + d->file.resize( 0 ); + } + else + { + server->sendURLData( d->loaderID, FINISHED, d->file ); + } + d->job = 0L; // signal KIO::Job::result deletes itself + server->removeDataJob( d->loaderID ); // will delete this + KJavaAppletServer::freeJavaServer(); +} + +void KJavaDownloader::jobCommand( int cmd ) +{ + if (!d->job) return; + switch (cmd) { + case KJAS_STOP: { + kdDebug(6100) << "jobCommand(" << d->loaderID << ") stop" << endl; + d->job->kill(); + d->job = 0L; // KIO::Job::kill deletes itself + KJavaAppletServer* server = KJavaAppletServer::allocateJavaServer(); + server->removeDataJob( d->loaderID ); // will delete this + KJavaAppletServer::freeJavaServer(); + break; + } + case KJAS_HOLD: + kdDebug(6100) << "jobCommand(" << d->loaderID << ") hold" << endl; + d->job->suspend(); + break; + case KJAS_RESUME: + kdDebug(6100) << "jobCommand(" << d->loaderID << ") resume" << endl; + d->job->resume(); + break; + } +} + +//----------------------------------------------------------------------------- + +class KJavaUploaderPrivate +{ +public: + KJavaUploaderPrivate() {} + ~KJavaUploaderPrivate() + { + delete url; + if (job) job->kill(); // KIO::Job::kill deletes itself + } + int loaderID; + KURL* url; + QByteArray file; + KIO::TransferJob* job; + bool finished; +}; + +KJavaUploader::KJavaUploader( int ID, const QString& url ) +{ + kdDebug(6100) << "KJavaUploader(" << ID << ") = " << url << endl; + + d = new KJavaUploaderPrivate; + + d->loaderID = ID; + d->url = new KURL( url ); + d->job = 0L; + d->finished = false; +} + +void KJavaUploader::start() +{ + kdDebug(6100) << "KJavaUploader::start(" << d->loaderID << ")" << endl; + KJavaAppletServer* server = KJavaAppletServer::allocateJavaServer(); + // create a suspended job + d->job = KIO::put( *d->url, -1, false, false, false ); + d->job->suspend(); + connect( d->job, SIGNAL(dataReq( KIO::Job*, QByteArray& )), + this, SLOT(slotDataRequest( KIO::Job*, QByteArray& )) ); + connect( d->job, SIGNAL(result(KIO::Job*)), + this, SLOT(slotResult(KIO::Job*)) ); + server->sendURLData( d->loaderID, CONNECTED, d->file ); + KJavaAppletServer::freeJavaServer(); +} + +KJavaUploader::~KJavaUploader() +{ + delete d; +} + +void KJavaUploader::slotDataRequest( KIO::Job*, QByteArray& qb ) +{ + // send our data and suspend + kdDebug(6100) << "slotDataRequest(" << d->loaderID << ") finished:" << d->finished << endl; + qb.resize( d->file.size() ); + KJavaAppletServer* server = KJavaAppletServer::allocateJavaServer(); + if (d->file.size() == 0) { + d->job = 0L; // eof, job deletes itself + server->removeDataJob( d->loaderID ); // will delete this + } else { + memcpy( qb.data(), d->file.data(), d->file.size() ); + d->file.resize( 0 ); + if (!d->finished) { + server->sendURLData( d->loaderID, REQUESTDATA, d->file ); + d->job->suspend(); + } + } + KJavaAppletServer::freeJavaServer(); +} + +void KJavaUploader::data( const QByteArray& qb ) +{ + kdDebug(6100) << "KJavaUploader::data(" << d->loaderID << ")" << endl; + d->file.resize( qb.size() ); + memcpy( d->file.data(), qb.data(), qb.size() ); + d->job->resume(); +} + +void KJavaUploader::slotResult( KIO::Job* ) +{ + kdDebug(6100) << "slotResult(" << d->loaderID << ") job:" << d->job << endl; + + if (!d->job) + return; + KJavaAppletServer* server = KJavaAppletServer::allocateJavaServer(); + if (d->job->error()) + { + int code = d->job->error(); + QString codestr = QString::number(code); + d->file.resize(codestr.length()); + memcpy( d->file.data(), codestr.ascii(), codestr.length() ); + kdDebug(6100) << "slave had an error " << code << ": " << d->job->errorString() << endl; + + server->sendURLData( d->loaderID, ERRORCODE, d->file ); + d->file.resize( 0 ); + } + else // shouldn't come here + kdError(6100) << "slotResult(" << d->loaderID << ") job:" << d->job << endl; + d->job = 0L; // signal KIO::Job::result deletes itself + server->removeDataJob( d->loaderID ); // will delete this + KJavaAppletServer::freeJavaServer(); +} + +void KJavaUploader::jobCommand( int cmd ) +{ + if (!d->job) return; + switch (cmd) { + case KJAS_STOP: { + kdDebug(6100) << "jobCommand(" << d->loaderID << ") stop" << endl; + d->finished = true; + if (d->job->isSuspended()) + d->job->resume(); + break; + } + } +} + +#include "kjavadownloader.moc" diff --git a/khtml/java/kjavadownloader.h b/khtml/java/kjavadownloader.h new file mode 100644 index 000000000..f49d8fe0c --- /dev/null +++ b/khtml/java/kjavadownloader.h @@ -0,0 +1,90 @@ +/* This file is part of the KDE project + * + * Copyright (C) 2000 Wynn Wilkes <wynnw@caldera.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef KJAVADOWNLOADER_H +#define KJAVADOWNLOADER_H + +#include <qobject.h> + +/** + * @short A class for handling downloads from KIO + * + * This class handles a KIO::get job and passes the data + * back to the AppletServer. + * + * @author Wynn Wilkes, wynnw@calderasystems.com + */ + +namespace KIO { + class Job; +} + +class KJavaDownloaderPrivate; +class KJavaUploaderPrivate; + +class KJavaKIOJob : public QObject +{ +Q_OBJECT +public: + virtual ~KJavaKIOJob(); + virtual void jobCommand( int cmd ) = 0; + virtual void data( const QByteArray& qb ); +}; + +class KJavaDownloader : public KJavaKIOJob +{ +Q_OBJECT + +public: + KJavaDownloader( int ID, const QString& url ); + ~KJavaDownloader(); + + virtual void jobCommand( int cmd ); +protected slots: + void slotData( KIO::Job*, const QByteArray& ); + void slotConnected( KIO::Job* ); + void slotMimetype( KIO::Job*, const QString& ); + void slotResult( KIO::Job* ); + +private: + KJavaDownloaderPrivate* d; + +}; + +class KJavaUploader : public KJavaKIOJob +{ +Q_OBJECT + +public: + KJavaUploader( int ID, const QString& url ); + ~KJavaUploader(); + + virtual void jobCommand( int cmd ); + virtual void data( const QByteArray& qb ); + void start(); +protected slots: + void slotDataRequest( KIO::Job*, QByteArray& ); + void slotResult( KIO::Job* ); +private: + KJavaUploaderPrivate* d; + +}; +#endif diff --git a/khtml/java/kjavaprocess.cpp b/khtml/java/kjavaprocess.cpp new file mode 100644 index 000000000..cdc69bcc0 --- /dev/null +++ b/khtml/java/kjavaprocess.cpp @@ -0,0 +1,397 @@ +/* This file is part of the KDE project + * + * Copyright (C) 2000 Richard Moore <rich@kde.org> + * 2000 Wynn Wilkes <wynnw@caldera.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kjavaprocess.h" + +#include <kdebug.h> +#include <kio/kprotocolmanager.h> + +#include <qtextstream.h> +#include <qmap.h> + +#include <config.h> + +#include <unistd.h> +#include <qptrlist.h> + +class KJavaProcessPrivate +{ +friend class KJavaProcess; +private: + QString jvmPath; + QString classPath; + QString mainClass; + QString extraArgs; + QString classArgs; + QPtrList<QByteArray> BufferList; + QMap<QString, QString> systemProps; + bool processKilled; +}; + +KJavaProcess::KJavaProcess() : KProcess() +{ + d = new KJavaProcessPrivate; + d->BufferList.setAutoDelete( true ); + d->processKilled = false; + + javaProcess = this; //new KProcess(); + + connect( javaProcess, SIGNAL( wroteStdin( KProcess * ) ), + this, SLOT( slotWroteData() ) ); + connect( javaProcess, SIGNAL( receivedStdout( int, int& ) ), + this, SLOT( slotReceivedData(int, int&) ) ); + connect( javaProcess, SIGNAL( processExited (KProcess *) ), + this, SLOT( slotExited (KProcess *) ) ); + + d->jvmPath = "java"; + d->mainClass = "-help"; +} + +KJavaProcess::~KJavaProcess() +{ + if ( isRunning() ) + { + kdDebug(6100) << "stopping java process" << endl; + stopJava(); + } + + //delete javaProcess; + delete d; +} + +bool KJavaProcess::isRunning() +{ + return javaProcess->isRunning(); +} + +bool KJavaProcess::startJava() +{ + return invokeJVM(); +} + +void KJavaProcess::stopJava() +{ + killJVM(); +} + +void KJavaProcess::setJVMPath( const QString& path ) +{ + d->jvmPath = path; +} + +void KJavaProcess::setClasspath( const QString& classpath ) +{ + d->classPath = classpath; +} + +void KJavaProcess::setSystemProperty( const QString& name, + const QString& value ) +{ + d->systemProps.insert( name, value ); +} + +void KJavaProcess::setMainClass( const QString& className ) +{ + d->mainClass = className; +} + +void KJavaProcess::setExtraArgs( const QString& args ) +{ + d->extraArgs = args; +} + +void KJavaProcess::setClassArgs( const QString& args ) +{ + d->classArgs = args; +} + +//Private Utility Functions used by the two send() methods +QByteArray* KJavaProcess::addArgs( char cmd_code, const QStringList& args ) +{ + //the buffer to store stuff, etc. + QByteArray* const buff = new QByteArray(); + QTextOStream output( *buff ); + const char sep = 0; + + //make space for the command size: 8 characters... + const QCString space( " " ); + output << space; + + //write command code + output << cmd_code; + + //store the arguments... + if( args.isEmpty() ) + { + output << sep; + } + else + { + QStringList::ConstIterator it = args.begin(); + const QStringList::ConstIterator itEnd = args.end(); + for( ; it != itEnd; ++it ) + { + if( !(*it).isEmpty() ) + { + output << (*it).local8Bit(); + } + output << sep; + } + } + + return buff; +} + +void KJavaProcess::storeSize( QByteArray* buff ) +{ + const int size = buff->size() - 8; //subtract out the length of the size_str + const QString size_str = QString("%1").arg( size, 8 ); + kdDebug(6100) << "KJavaProcess::storeSize, size = " << size_str << endl; + + const char* size_ptr = size_str.latin1(); + for( int i = 0; i < 8; ++i ) + buff->at(i) = size_ptr[i]; +} + +void KJavaProcess::sendBuffer( QByteArray* buff ) +{ + d->BufferList.append( buff ); + if( d->BufferList.count() == 1) + { + popBuffer(); + } +} + +void KJavaProcess::send( char cmd_code, const QStringList& args ) +{ + if( isRunning() ) + { + QByteArray* const buff = addArgs( cmd_code, args ); + storeSize( buff ); + kdDebug(6100) << "<KJavaProcess::send " << (int)cmd_code << endl; + sendBuffer( buff ); + } +} + +void KJavaProcess::send( char cmd_code, const QStringList& args, + const QByteArray& data ) +{ + if( isRunning() ) + { + kdDebug(6100) << "KJavaProcess::send, qbytearray is size = " << data.size() << endl; + + QByteArray* const buff = addArgs( cmd_code, args ); + const int cur_size = buff->size(); + const int data_size = data.size(); + buff->resize( cur_size + data_size ); + memcpy( buff->data() + cur_size, data.data(), data_size ); + + storeSize( buff ); + sendBuffer( buff ); + } +} + +void KJavaProcess::popBuffer() +{ + QByteArray* const buf = d->BufferList.first(); + if( buf ) + { +// DEBUG stuff... +// kdDebug(6100) << "Sending buffer to java, buffer = >>"; +// for( unsigned int i = 0; i < buf->size(); i++ ) +// { +// if( buf->at(i) == (char)0 ) +// kdDebug(6100) << "<SEP>"; +// else if( buf->at(i) > 0 && buf->at(i) < 10 ) +// kdDebug(6100) << "<CMD " << (int) buf->at(i) << ">"; +// else +// kdDebug(6100) << buf->at(i); +// } +// kdDebug(6100) << "<<" << endl; + + //write the data + if ( !javaProcess->writeStdin( buf->data(), + buf->size() ) ) + { + kdError(6100) << "Could not write command" << endl; + } + } +} + +void KJavaProcess::slotWroteData( ) +{ + //do this here- we can't free the data until we know it went through + d->BufferList.removeFirst(); //this should delete it since we setAutoDelete(true) + kdDebug(6100) << "slotWroteData " << d->BufferList.count() << endl; + + if ( !d->BufferList.isEmpty() ) + { + popBuffer(); + } +} + + +bool KJavaProcess::invokeJVM() +{ + + *javaProcess << d->jvmPath; + + if( !d->classPath.isEmpty() ) + { + *javaProcess << "-classpath"; + *javaProcess << d->classPath; + } + + //set the system properties, iterate through the qmap of system properties + QMap<QString,QString>::ConstIterator it = d->systemProps.begin(); + const QMap<QString,QString>::ConstIterator itEnd = d->systemProps.end(); + + for( ; it != itEnd; ++it ) + { + QString currarg; + + if( !it.key().isEmpty() ) + { + currarg = "-D" + it.key(); + if( !it.data().isEmpty() ) + currarg += "=" + it.data(); + } + + if( !currarg.isEmpty() ) + *javaProcess << currarg; + } + + //load the extra user-defined arguments + if( !d->extraArgs.isEmpty() ) + { + // BUG HERE: if an argument contains space (-Dname="My name") + // this parsing will fail. Need more sophisticated parsing -- use KShell? + const QStringList args = QStringList::split( " ", d->extraArgs ); + QStringList::ConstIterator it = args.begin(); + const QStringList::ConstIterator itEnd = args.end(); + for ( ; it != itEnd; ++it ) + *javaProcess << *it; + } + + *javaProcess << d->mainClass; + + if ( !d->classArgs.isNull() ) + *javaProcess << d->classArgs; + + kdDebug(6100) << "Invoking JVM now...with arguments = " << endl; + QString argStr; + QTextOStream stream( &argStr ); + const QValueList<QCString> args = javaProcess->args(); + qCopy( args.begin(), args.end(), QTextOStreamIterator<QCString>( stream, " " ) ); + kdDebug(6100) << argStr << endl; + + KProcess::Communication flags = (KProcess::Communication) + (KProcess::Stdin | KProcess::Stdout | + KProcess::NoRead); + + const bool rval = javaProcess->start( KProcess::NotifyOnExit, flags ); + if( rval ) + javaProcess->resume(); //start processing stdout on the java process + else + killJVM(); + + return rval; +} + +void KJavaProcess::killJVM() +{ + d->processKilled = true; + disconnect( javaProcess, SIGNAL( receivedStdout( int, int& ) ), + this, SLOT( slotReceivedData(int, int&) ) ); + javaProcess->kill(); +} + +void KJavaProcess::flushBuffers() +{ + while ( !d->BufferList.isEmpty() ) { + if (innot) + slotSendData(0); + else + d->BufferList.removeFirst(); //note: AutoDelete is true + } +} + +/* In this method, read one command and send it to the d->appletServer + * then return, so we don't block the event handling + */ +void KJavaProcess::slotReceivedData( int fd, int& len ) +{ + //read out the length of the message, + //read the message and send it to the applet server + char length[9] = { 0 }; + const int num_bytes = ::read( fd, length, 8 ); + if( !num_bytes ) + { + len = 0; + return; + } + if( num_bytes == -1 ) + { + kdError(6100) << "could not read 8 characters for the message length!!!!" << endl; + len = 0; + return; + } + + const QString lengthstr( length ); + bool ok; + const int num_len = lengthstr.toInt( &ok ); + if( !ok ) + { + kdError(6100) << "could not parse length out of: " << lengthstr << endl; + len = num_bytes; + return; + } + + //now parse out the rest of the message. + char* const msg = new char[num_len]; + const int num_bytes_msg = ::read( fd, msg, num_len ); + if( num_bytes_msg == -1 || num_bytes_msg != num_len ) + { + kdError(6100) << "could not read the msg, num_bytes_msg = " << num_bytes_msg << endl; + delete[] msg; + len = num_bytes; + return; + } + + QByteArray qb; + emit received( qb.duplicate( msg, num_len ) ); + delete[] msg; + len = num_bytes + num_bytes_msg; +} + +void KJavaProcess::slotExited( KProcess *process ) +{ + if (process == javaProcess) { + int status = -1; + if (!d->processKilled) { + status = javaProcess->exitStatus(); + } + kdDebug(6100) << "jvm exited with status " << status << endl; + emit exited(status); + } +} + +#include "kjavaprocess.moc" diff --git a/khtml/java/kjavaprocess.h b/khtml/java/kjavaprocess.h new file mode 100644 index 000000000..566e8fda5 --- /dev/null +++ b/khtml/java/kjavaprocess.h @@ -0,0 +1,164 @@ +// -*- c++ -*- + +/* This file is part of the KDE project + * + * Copyright (C) 2000 Richard Moore <rich@kde.org> + * 2000 Wynn Wilkes <wynnw@caldera.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KJAVAPROCESS_H +#define KJAVAPROCESS_H + +#include <kprocess.h> +#include <qcstring.h> + +/** + * @short A class for invoking a Java VM + * + * This class is a general tool for invoking a Java interpreter. It allows you + * to specify some of the standard options that should be understood by all + * JVMs. + * + * @author Richard J. Moore, rich@kde.org + * @author Wynn Wilkes, wynnw@calderasystems.com + */ + +class KJavaProcessPrivate; +class KJavaProcess : public KProcess //QObject +{ +Q_OBJECT + +public: + /** + * Creates a process object, the process is NOT invoked at this point. + * You should first set the process's parameters, and then call startJava. + */ + KJavaProcess(); + virtual ~KJavaProcess(); + + /** + * Invoke the JVM with the parameters that have been set. The Java process + * will start after this call. + */ + bool startJava(); + + /** + * Stop the JVM (if it's running). + */ + void stopJava(); + + /** + * Returns the status of the java Process- true if it's ok, false if it has died. + * It calls KProcess::isRunning() + */ + bool isRunning(); + + /** + * Used to specify the path to the Java executable to be run. + */ + void setJVMPath( const QString& path ); + + /** + * This will set the classpath the Java process will use. It's used as a the + * -cp command line option. It adds every jar file stored in $KDEDIRS/share/apps/kjava/ + * to the classpath, and then adds the $CLASSPATH environmental variable. This allows + * users to simply drop the JSSE (Java Secure Sockets Extension classes into that directory + * without having to modify the jvm configuration files. + */ + void setClasspath( const QString& classpath ); + + /** + * Set a property on the java command line as -Dname=value, or -Dname if value is QString::null. + * For example, you could call setSystemProperty( "kjas.debug", "" ) to set the kjas.debug property. + */ + void setSystemProperty( const QString& name, const QString& value ); + + /** + * The class to be called when startJava() is called. + */ + void setMainClass( const QString& clazzName ); + + /** + * Extra flags passed to the JVM. + */ + void setExtraArgs( const QString& args ); + + /** + * Arguments passed to the main class. They will be very last in the java + * command line, after the main class. + */ + void setClassArgs( const QString& classArgs ); + + /** + * Sends a command to the KJAS Applet Server by building a QByteArray + * out of the data, and then writes it standard out. + */ + void send( char cmd_code, const QStringList& args ); + + /** + * Sends a command to the KJAS Applet Server by building a QByteArray + * out of the data, and then writes it standard out. It adds each QString + * in the arg list, and then adds the data array. + */ + void send( char cmd_code, const QStringList& args, const QByteArray& data ); + + /** + * Writes all pending data to JVM + **/ + void flushBuffers(); + +protected slots: + /** + * This slot is called whenever something is written to stdin of the process. + * It's called again to make sure we keep emptying out the buffer that contains + * the messages we need send. + */ + void slotWroteData(); + + /** + * This slot is called when the Java Process writes to standard out. We then + * process the data from the file descriptor that is passed to us and send the + * command to the AppletServer + */ + void slotReceivedData( int, int& ); + /** + * This slot is called when the Java Process exited. + */ + void slotExited( KProcess *process ); + +protected: + virtual bool invokeJVM(); + virtual void killJVM(); + + QByteArray* addArgs( char cmd_code, const QStringList& args ); + void popBuffer(); + void sendBuffer( QByteArray* buff ); + void storeSize( QByteArray* buff ); + + KProcess* javaProcess; + +signals: + void received( const QByteArray& ); + void exited( int status ); + +private: + KJavaProcessPrivate *d; + +}; + +#endif // KJAVAPROCESS_H diff --git a/khtml/java/kqeventutil.cpp b/khtml/java/kqeventutil.cpp new file mode 100644 index 000000000..588051617 --- /dev/null +++ b/khtml/java/kqeventutil.cpp @@ -0,0 +1,200 @@ +// -*- c++ -*- +/* This file is part of the KDE project + * + * Copyright (C) 2000 Wynn Wilkes <wynnw@caldera.com> + * 2002 Till Krech <till@snafu.de> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#include <qevent.h> +#include "kqeventutil.h" + +QString KQEventUtil::getQtEventName( QEvent* e ) +{ + QString s; + + switch( e->type() ) + { + case QEvent::None: + s = "None"; + break; + case QEvent::Timer: + s = "Timer"; + break; + case QEvent::MouseButtonPress: + s = "MouseButtonPress"; + break; + case QEvent::MouseButtonRelease: + s = "MouseButtonRelease"; + break; + case QEvent::MouseButtonDblClick: + s = "MouseButtonClick"; + break; + case QEvent::MouseMove: + s = "MouseMove"; + break; + case QEvent::KeyPress: + s = "KeyPress"; + break; + case QEvent::KeyRelease: + s = "KeyRelease"; + break; + case QEvent::FocusIn: + s = "FocusIn"; + break; + case QEvent::FocusOut: + s = "FocusOut"; + break; + case QEvent::Enter: + s = "Enter"; + break; + case QEvent::Leave: + s = "Leave"; + break; + case QEvent::Paint: + s = "Paint"; + break; + case QEvent::Move: + s = "Move"; + break; + case QEvent::Resize: + s = "Resize"; + break; + case QEvent::Create: + s = "Create"; + break; + case QEvent::Destroy: + s = "Destroy"; + break; + case QEvent::Show: + s = "Show"; + break; + case QEvent::Hide: + s = "Hide"; + break; + case QEvent::Close: + s = "Close"; + break; + case QEvent::Quit: + s = "Quit"; + break; + case QEvent::Reparent: + s = "Reparent"; + break; + case QEvent::ShowMinimized: + s = "ShowMinimized"; + break; + case QEvent::ShowNormal: + s = "ShowNormal"; + break; + case QEvent::WindowActivate: + s = "WindowActivate"; + break; + case QEvent::WindowDeactivate: + s = "WindowDeactivate"; + break; + case QEvent::ShowToParent: + s = "ShowToParent"; + break; + case QEvent::HideToParent: + s = "HideToParent"; + break; + case QEvent::ShowMaximized: + s = "ShowMaximized"; + break; + case QEvent::Accel: + s = "Accel"; + break; + case QEvent::Wheel: + s = "Wheel"; + break; + case QEvent::AccelAvailable: + s = "AccelAvailable"; + break; + case QEvent::CaptionChange: + s = "CaptionChange"; + break; + case QEvent::IconChange: + s = "IconChange"; + break; + case QEvent::ParentFontChange: + s = "ParentFontChange"; + break; + case QEvent::ApplicationFontChange: + s = "ApplicationFontChange"; + break; + case QEvent::ParentPaletteChange: + s = "ParentPaletteChange"; + break; + case QEvent::ApplicationPaletteChange: + s = "ApplicationPaletteChange"; + break; + case QEvent::Clipboard: + s = "Clipboard"; + break; + case QEvent::Speech: + s = "Speech"; + break; + case QEvent::SockAct: + s = "SockAct"; + break; + case QEvent::AccelOverride: + s = "AccelOverride"; + break; + case QEvent::DragEnter: + s = "DragEnter"; + break; + case QEvent::DragMove: + s = "DragMove"; + break; + case QEvent::DragLeave: + s = "DragLeave"; + break; + case QEvent::Drop: + s = "Drop"; + break; + case QEvent::DragResponse: + s = "DragResponse"; + break; + case QEvent::ChildInserted: + s = "ChildInserted"; + break; + case QEvent::ChildRemoved: + s = "ChildRemoved"; + break; + case QEvent::LayoutHint: + s = "LayoutHint"; + break; + case QEvent::ShowWindowRequest: + s = "ShowWindowRequest"; + break; + case QEvent::ActivateControl: + s = "ActivateControl"; + break; + case QEvent::DeactivateControl: + s = "DeactivateControl"; + break; + case QEvent::User: + s = "User Event"; + break; + + default: + s = "Undefined Event, value = " + QString::number( e->type() ); + break; + } + + return s; +} diff --git a/khtml/java/kqeventutil.h b/khtml/java/kqeventutil.h new file mode 100644 index 000000000..fd199baa1 --- /dev/null +++ b/khtml/java/kqeventutil.h @@ -0,0 +1,34 @@ +// -*- c++ -*- +/* This file is part of the KDE project + * + * Copyright (C) 2000 Wynn Wilkes <wynnw@caldera.com> + * 2002 Till Krech <till@snafu.de> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KQEVENTUTIL_H +#define KQEVENTUTIL_H + +#include <qstring.h> +class QEvent; + +class KQEventUtil { + public: + static QString getQtEventName(QEvent *e); +}; + +#endif diff --git a/khtml/java/kxeventutil.cpp b/khtml/java/kxeventutil.cpp new file mode 100644 index 000000000..0eea8a744 --- /dev/null +++ b/khtml/java/kxeventutil.cpp @@ -0,0 +1,408 @@ +// -*- c++ -*- +/* This file is part of the KDE project + * + * Copyright (C) 2002 Till Krech <till@snafu.de> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <X11/X.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xatom.h> +#include <qstring.h> +#include <qstringlist.h> + +#include "kxeventutil.h" + +QString KXEventUtil::getXAnyEventInfo(XEvent *xevent) { + XAnyEvent *e = &xevent->xany; + QString winname("window"); + switch (e->type) { + case GraphicsExpose: + case NoExpose: + winname="drawable"; + break; + case CreateNotify: + case ConfigureRequest: + winname="parent"; + break; + case DestroyNotify: + case ConfigureNotify: + case MapNotify: + case ReparentNotify: + case UnmapNotify: + winname="event"; + default: + break; + } + QString s("serial=%1 send_event=%2 display=0x%3 %4=%5"); + return + s.arg(e->serial) + .arg(e->send_event) + .arg((long)e->display, 0, 16) + .arg(winname) + .arg(e->window); +} +QString KXEventUtil::getXButtonEventInfo(XEvent *xevent) { + XButtonEvent *e = &xevent->xbutton; + QString s("root=%1 subwindow=%2 time=%3 x=%4 y=%5 x_root=%6 y_root=%7 state=%8 button=%9"); + QString t(" same_screen=%1"); + return + s.arg(e->root) + .arg(e->subwindow) + .arg(e->time) + .arg(e->x) + .arg(e->y) + .arg(e->x_root) + .arg(e->y_root) + .arg(e->state) + .arg(e->button) + +t.arg(e->same_screen); +} + +QString KXEventUtil::getXKeyEventInfo(XEvent *xevent) { + XKeyEvent *e = &xevent->xkey; + QString s("root=%1 subwindow=%2 time=%3 x=%4 y=%5 x_root=%6 y_root=%7 state=%8 keycode=%9"); + QString t(" same_screen=%1"); + return + s.arg(e->root) + .arg(e->subwindow) + .arg(e->time) + .arg(e->x) + .arg(e->y) + .arg(e->x_root) + .arg(e->y_root) + .arg(e->state) + .arg(e->keycode) + +t.arg(e->same_screen); +} + +QString KXEventUtil::getXMotionEventInfo(XEvent *xevent) { + XMotionEvent *e = &xevent->xmotion; + QString s("root=%1 subwindow=%2 time=%3 x=%4 y=%5 x_root=%6 y_root=%7 state=%8 is_hint=%9"); + QString t(" same_screen=%1"); + return + s.arg(e->root) + .arg(e->subwindow) + .arg(e->time) + .arg(e->x) + .arg(e->y) + .arg(e->x_root) + .arg(e->y_root) + .arg(e->state) + .arg(e->is_hint) + +t.arg(e->same_screen); +} +QString KXEventUtil::getXCrossingEventInfo(XEvent *xevent) { + XCrossingEvent *e = &xevent->xcrossing; + QString ms, ds; + switch (e->mode) { + case NotifyNormal: ms = "NotifyNormal"; break; + case NotifyGrab: ms = "NotifyGrab"; break; + case NotifyUngrab: ms = "NotifyUngrab"; break; + default: ms="?"; + } + switch (e->detail) { + case NotifyAncestor: ds = "NotifyAncestor"; break; + case NotifyVirtual: ds = "NotifyVirtual"; break; + case NotifyInferior: ds = "NotifyInferior"; break; + case NotifyNonlinear: ds = "NotifyNonlinear"; break; + case NotifyNonlinearVirtual: ds = "NotifyNonlinearVirtual"; break; + default: ds="?"; + } + + QString s("root=%1 subwindow=%2 time=%3 x=%4 y=%5 x_root=%6 y_root=%7 mode=%8=%9 "); + QString t("detail=%1=%2 same_screen=%3 focus=%4 state=%5"); + return + s.arg(e->root) + .arg(e->subwindow) + .arg(e->time) + .arg(e->x) + .arg(e->y) + .arg(e->x_root) + .arg(e->y_root) + .arg(e->mode).arg(ms) + + + t.arg(e->detail).arg(ds) + .arg(e->same_screen) + .arg(e->focus) + .arg(e->state); +} +QString KXEventUtil::getXFocusChangeEventInfo(XEvent *xevent) { + XFocusChangeEvent *e = &xevent->xfocus; + QString s("mode=%1 detail=%2"); + return + s.arg(e->mode) + .arg(e->detail); +} +QString KXEventUtil::getXExposeEventInfo(XEvent *xevent) { + XExposeEvent *e = &xevent->xexpose; + QString s("x=%1 y=%2 width=%3 height=%4 count=%5"); + return + s.arg(e->x) + .arg(e->y) + .arg(e->width) + .arg(e->height) + .arg(e->count); +} + + + +QString KXEventUtil::getXGraphicsExposeEventInfo(XEvent *xevent) { + XGraphicsExposeEvent *e = &xevent->xgraphicsexpose; + QString s("x=%1 y=%2 width=%3 height=%4 count=%5 major_code=%6 minor_code=%7"); + return + s.arg(e->x) + .arg(e->y) + .arg(e->width) + .arg(e->height) + .arg(e->count) + .arg(e->major_code) + .arg(e->minor_code); +} +QString KXEventUtil::getXNoExposeEventInfo(XEvent *xevent) { + XNoExposeEvent *e = &xevent->xnoexpose; + QString s("major_code=%1 minor_code=%2"); + return + s.arg(e->major_code) + .arg(e->minor_code); +} + + +QString KXEventUtil::getXCreateWindowEventInfo(XEvent *xevent) { + XCreateWindowEvent *e = &xevent->xcreatewindow; + QString s("window=%1 x=%2 y=%3 width=%4 height=%5 border_width=%6 override_redirect=%7"); + return + s.arg(e->window) + .arg(e->x) + .arg(e->y) + .arg(e->width) + .arg(e->height) + .arg(e->border_width) + .arg(e->override_redirect); +} + +QString KXEventUtil::getXDestroyWindowEventInfo(XEvent *xevent) { + XDestroyWindowEvent *e = &xevent->xdestroywindow; + QString s("window=%1"); + return + s.arg(e->window); +} +QString KXEventUtil::getXMapEventInfo(XEvent *xevent) { + XMapEvent *e = &xevent->xmap; + QString s("window=%1 override_redirect=%2"); + return + s.arg(e->window) + .arg(e->override_redirect); +} +QString KXEventUtil::getXMappingEventInfo(XEvent *xevent) { + XMappingEvent *e = &xevent->xmapping; + QString s("request=%1 first_keycode=%2 count=%3"); + return + s.arg(e->request) + .arg(e->first_keycode) + .arg(e->count); +} +QString KXEventUtil::getXReparentEventInfo(XEvent *xevent) { + XReparentEvent *e = &xevent->xreparent; + QString s("window=%1 parent=%2 x=%3 y=%4"); + return + s.arg(e->window) + .arg(e->parent) + .arg(e->x) + .arg(e->y); +} +QString KXEventUtil::getXUnmapEventInfo(XEvent *xevent) { + XUnmapEvent *e = &xevent->xunmap; + QString s("window=%1 from_configure=%2"); + return + s.arg(e->window) + .arg(e->from_configure); +} + +QString KXEventUtil::getXConfigureEventInfo(XEvent *xevent) { + XConfigureEvent *e = &xevent->xconfigure; + QString s("window=%1 x=%2 y=%2 width=%3 height=%4 border_width=%5 above=%6 override_redirect=%7"); + return + s.arg(e->window) + .arg(e->x).arg(e->y) + .arg(e->width).arg(e->height) + .arg(e->border_width) + .arg(e->above) + .arg(e->override_redirect); +} + +QString KXEventUtil::getXConfigureRequestEventInfo(XEvent *xevent) { + XConfigureRequestEvent *e = &xevent->xconfigurerequest; + unsigned vm = e->value_mask; + QStringList vml; + if (vm & CWX) vml.append("CWX"); + if (vm & CWY) vml.append("CWY"); + if (vm & CWWidth) vml.append("CWWidth"); + if (vm & CWHeight) vml.append("CWHeight"); + if (vm & CWBorderWidth) vml.append("CWBorderWidth"); + if (vm & CWSibling) vml.append("CWSibling"); + if (vm & CWStackMode) vml.append("CWStackMode"); + QString vms = vml.join("|"); + QString s("window=%1 x=%2 y=%2 width=%3 height=%4 border_width=%5 above=%6 detail=%7 value_mask=0x%8=%9"); + return + s.arg(e->window) + .arg(e->x).arg(e->y) + .arg(e->width).arg(e->height) + .arg(e->border_width) + .arg(e->above) + .arg(e->detail) + .arg(e->value_mask, 0, 16) + .arg(vms); +} +QString KXEventUtil::getX11EventInfo( XEvent* e ) +{ + QString anyInfo = getXAnyEventInfo(e); + QString info = ""; + QString s; + switch( e->type ) + { + case KeyPress: + s = "KeyPress"; + info = getXKeyEventInfo(e); + break; + case KeyRelease: + s = "KeyRelease"; + info = getXKeyEventInfo(e); + break; + case ButtonPress: + s = "ButtonPress"; + info = getXButtonEventInfo(e); + break; + case ButtonRelease: + s = "ButtonRelease"; + info = getXButtonEventInfo(e); + break; + case MotionNotify: + s = "MotionNotify"; + info = getXMotionEventInfo(e); + break; + case EnterNotify: + s = "EnterNotify"; + info = getXCrossingEventInfo(e); + break; + case LeaveNotify: + s = "LeaveNotify"; + info = getXCrossingEventInfo(e); + break; + case FocusIn: + s = "FocusIn"; + info = getXFocusChangeEventInfo(e); + break; + case FocusOut: + s = "FocusOut"; + info = getXFocusChangeEventInfo(e); + break; + case KeymapNotify: + s = "KeymapNotify"; + break; + case Expose: + s = "Expose"; + info = getXExposeEventInfo(e); + break; + case GraphicsExpose: + s = "GraphicsExpose"; + info = getXGraphicsExposeEventInfo(e); + break; + case NoExpose: + info = getXNoExposeEventInfo(e); + s = "NoExpose"; + break; + case VisibilityNotify: + s = "VisibilityNotify"; + break; + case CreateNotify: + s = "CreateNotify"; + info = getXCreateWindowEventInfo(e); + break; + case DestroyNotify: + s = "DestroyNotify"; + info = getXDestroyWindowEventInfo(e); + break; + case UnmapNotify: + s = "UnmapNotify"; + info = getXUnmapEventInfo(e); + break; + case MapNotify: + s = "MapNotify"; + info = getXMapEventInfo(e); + break; + case MapRequest: + s = "MapRequest"; + break; + case ReparentNotify: + s = "ReparentNotify"; + info = getXReparentEventInfo(e); + break; + case ConfigureNotify: + s = "ConfigureNotify"; + info = getXConfigureEventInfo(e); + break; + case ConfigureRequest: + s = "ConfigureRequest"; + info = getXConfigureRequestEventInfo(e); + break; + case GravityNotify: + s = "GravityNotify"; + break; + case ResizeRequest: + s = "ResizeRequest"; + break; + case CirculateNotify: + s = "CirculateNofify"; + break; + case CirculateRequest: + s = "CirculateRequest"; + break; + case PropertyNotify: + s = "PropertyNotify"; + break; + case SelectionClear: + s = "SelectionClear"; + break; + case SelectionRequest: + s = "SelectionRequest"; + break; + case SelectionNotify: + s = "SelectionNotify"; + break; + case ColormapNotify: + s = "ColormapNotify"; + break; + case ClientMessage: + s = "ClientMessage"; + break; + case MappingNotify: + s = "MappingNotify"; + info = getXMappingEventInfo(e); + break; + case LASTEvent: + s = "LASTEvent"; + break; + + default: + s = "Undefined"; + break; + } + + return s + " " + anyInfo + " " + info; +} diff --git a/khtml/java/kxeventutil.h b/khtml/java/kxeventutil.h new file mode 100644 index 000000000..ea5a49bd1 --- /dev/null +++ b/khtml/java/kxeventutil.h @@ -0,0 +1,51 @@ +// -*- c++ -*- +/* This file is part of the KDE project + * + * Copyright (C) 2002 Till Krech <till@snafu.de> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifndef KXEVENTUTIL_H +#define KXEVENTUTIL_H + +#include <X11/X.h> +#include <X11/Xlib.h> +#include <qstring.h> + +class KXEventUtil { + public: + static QString getXEventName(XEvent *e); + static QString getXAnyEventInfo(XEvent *xevent); + static QString getXButtonEventInfo(XEvent *xevent); + static QString getXKeyEventInfo(XEvent *xevent); + static QString getXMotionEventInfo(XEvent *xevent); + static QString getXCrossingEventInfo(XEvent *xevent); + static QString getXFocusChangeEventInfo(XEvent *xevent); + static QString getXExposeEventInfo(XEvent *xevent); + static QString getXGraphicsExposeEventInfo(XEvent *xevent); + static QString getXNoExposeEventInfo(XEvent *xevent); + static QString getXCreateWindowEventInfo(XEvent *xevent); + static QString getXDestroyWindowEventInfo(XEvent *xevent); + static QString getXMapEventInfo(XEvent *xevent); + static QString getXMappingEventInfo(XEvent *xevent); + static QString getXReparentEventInfo(XEvent *xevent); + static QString getXUnmapEventInfo(XEvent *xevent); + static QString getXConfigureEventInfo(XEvent *xevent); + static QString getXConfigureRequestEventInfo(XEvent *xevent); + static QString getX11EventInfo( XEvent* e ); +}; + +#endif diff --git a/khtml/java/make-jar b/khtml/java/make-jar new file mode 100755 index 000000000..a817ec1f3 --- /dev/null +++ b/khtml/java/make-jar @@ -0,0 +1,12 @@ +#!/bin/sh +# very simple shell script to compile the java +# sources and generate the jar file +set -e # exit on error +set -x # be verbose +rm -rf java-build-dir/* +mkdir -p java-build-dir/images +cp images/*.gif images/*.png java-build-dir/images/ +pwd=`pwd`; +javac -d java-build-dir -deprecation -target 1.2 `find org/kde netscape -name '*.java'` \ + && cd java-build-dir && jar c0vf $pwd/kjava.jar images `find org/kde netscape -name *.class` + diff --git a/khtml/java/netscape/javascript/JSException.java b/khtml/java/netscape/javascript/JSException.java new file mode 100644 index 000000000..222033e7a --- /dev/null +++ b/khtml/java/netscape/javascript/JSException.java @@ -0,0 +1,20 @@ +package netscape.javascript; + +public class JSException extends Exception { + public JSException() {} + public JSException(String s) { + super(s); + } + public JSException(String s, String fn, int ln, String src, int ti) { + super(s); + filename = new String(fn); + linenumber = ln; + source = src; + tokenindex = ti; + } + private String filename = null; + private int linenumber; + private String source = null; + private int tokenindex; +} + diff --git a/khtml/java/netscape/javascript/JSObject.java b/khtml/java/netscape/javascript/JSObject.java new file mode 100644 index 000000000..f103d90c8 --- /dev/null +++ b/khtml/java/netscape/javascript/JSObject.java @@ -0,0 +1,21 @@ +package netscape.javascript; + +import java.applet.Applet; + +public abstract class JSObject extends Object { + protected JSObject() + { + } + public abstract Object call(String function, Object[] arguments) throws JSException; + public abstract Object eval(String script) throws JSException; + public abstract Object getMember(String name) throws JSException; + public abstract void setMember(String name, Object o) throws JSException; + public abstract void removeMember(String name) throws JSException; + public abstract Object getSlot(int index) throws JSException; + public abstract void setSlot(int index, Object o) throws JSException; + public static JSObject getWindow(Applet applet) throws JSException + { + return org.kde.javascript.JSObject.getWindow(applet, 0); + } +} + diff --git a/khtml/java/netscape/plugin/Plugin.java b/khtml/java/netscape/plugin/Plugin.java new file mode 100644 index 000000000..c7be0e542 --- /dev/null +++ b/khtml/java/netscape/plugin/Plugin.java @@ -0,0 +1,28 @@ +package netscape.plugin; + +import netscape.javascript.*; + +public class Plugin { + + public Plugin() { + System.out.println("Plugin.Plugin"); + } + public JSObject getWindow() throws JSException { + System.out.println("Plugin.getWindow"); + return JSObject.getWindow(null); + } + public void destroy() { + System.out.println("Plugin.destroy"); + } + public int getPeer() { + System.out.println("Plugin.getPeer"); + return 0; + } + public void init() { + System.out.println("Plugin.init"); + } + public boolean isActive() { + System.out.println("Plugin.isActive"); + return true; + } +} diff --git a/khtml/java/netscape/security/ForbiddenTargetException.java b/khtml/java/netscape/security/ForbiddenTargetException.java new file mode 100644 index 000000000..27879d05b --- /dev/null +++ b/khtml/java/netscape/security/ForbiddenTargetException.java @@ -0,0 +1,10 @@ +package netscape.security; +public class ForbiddenTargetException extends RuntimeException { + public ForbiddenTargetException() + { + } + public ForbiddenTargetException(String message) + { + super(message); + } +} diff --git a/khtml/java/netscape/security/Principal.java b/khtml/java/netscape/security/Principal.java new file mode 100644 index 000000000..1f93a8293 --- /dev/null +++ b/khtml/java/netscape/security/Principal.java @@ -0,0 +1,4 @@ +package netscape.security; + +public class Principal { +}
\ No newline at end of file diff --git a/khtml/java/netscape/security/PrivilegeManager.java b/khtml/java/netscape/security/PrivilegeManager.java new file mode 100644 index 000000000..c6555eaa0 --- /dev/null +++ b/khtml/java/netscape/security/PrivilegeManager.java @@ -0,0 +1,109 @@ +package netscape.security; + +public class PrivilegeManager extends Object { + public static final int PROPER_SUBSET = 1; + public static final int EQUAL = 2; + public static final int NO_SUBSET = 3; + public static final int SIGNED_APPLET_DBNAME = 4; + public static final int TEMP_FILENAME = 5; + + private static PrivilegeManager thePrivilegeManager = null; + + protected PrivilegeManager() + { + } + public void checkPrivilegeEnabled(netscape.security.Target target) throws netscape.security.ForbiddenTargetException + { + } + public void checkPrivilegeEnabled(netscape.security.Target target, Object o) throws netscape.security.ForbiddenTargetException + { + } + public static void enablePrivilege(String privilegeString) throws netscape.security.ForbiddenTargetException + { + } + public void enablePrivilege(netscape.security.Target target) throws netscape.security.ForbiddenTargetException + { + } + public void enablePrivilege(netscape.security.Target target, netscape.security.Principal principal) throws netscape.security.ForbiddenTargetException + { + } + public void enablePrivilege(netscape.security.Target target, netscape.security.Principal principal, Object o) throws netscape.security.ForbiddenTargetException + { + } + public void revertPrivilege(netscape.security.Target target) + { + } + public static void revertPrivilege(String privilegeString) + { + } + public void disablePrivilege(netscape.security.Target target) + { + } + public void disablePrivilege(String privilegeString) + { + } + public static void checkPrivilegeGranted(String privilegeString) throws netscape.security.ForbiddenTargetException + { + } + public void checkPrivilegeGranted(netscape.security.Target target) throws netscape.security.ForbiddenTargetException + { + } + public void checkPrivilegeGranted(netscape.security.Target target, Object o) throws netscape.security.ForbiddenTargetException + { + } + public void checkPrivilegeGranted(netscape.security.Target target, netscape.security.Principal principal, Object o) throws netscape.security.ForbiddenTargetException + { + } + public boolean isCalledByPrincipal(netscape.security.Principal principal, int dontknow) + { + return false; + } + public boolean isCalledByPrincipal(netscape.security.Principal principal) + { + return false; + } + public static netscape.security.Principal getSystemPrincipal() + { + return null; + } + public static netscape.security.PrivilegeManager getPrivilegeManager() + { + if (thePrivilegeManager == null) { + thePrivilegeManager = new PrivilegeManager(); + } + return thePrivilegeManager; + } + public boolean hasPrincipal(Class cl, netscape.security.Principal principal) + { + return true; + } + public int comparePrincipalArray(netscape.security.Principal[] a, netscape.security.Principal[] b) + { + return 1; + } + public boolean checkMatchPrincipal(Class cl, int dontknow) + { + return true; + } + public boolean checkMatchPrincipal(netscape.security.Principal principal, int dontknow) + { + return true; + } + public boolean checkMatchPrincipal(Class cl) + { + return true; + } + public boolean checkMatchPrincipalAlways() + { + return true; + } + public netscape.security.Principal[] getClassPrincipalsFromStack(int n) + { + return null; + } + /* + public netscape.security.PrivilegeTable getPrivilegeTableFromStack(); + { + } + */ +} diff --git a/khtml/java/netscape/security/Target.java b/khtml/java/netscape/security/Target.java new file mode 100644 index 000000000..8b50040b5 --- /dev/null +++ b/khtml/java/netscape/security/Target.java @@ -0,0 +1,4 @@ +package netscape.security; + +public class Target { +}
\ No newline at end of file diff --git a/khtml/java/org/kde/javascript/JSObject.java b/khtml/java/org/kde/javascript/JSObject.java new file mode 100644 index 000000000..11e7f9f99 --- /dev/null +++ b/khtml/java/org/kde/javascript/JSObject.java @@ -0,0 +1,183 @@ +package org.kde.javascript; + +import java.applet.Applet; +import org.kde.kjas.server.KJASAppletContext; +import org.kde.kjas.server.Main; + +public class JSObject extends netscape.javascript.JSObject { + public String returnvalue = null; + public Thread thread; + + private String jsobject; + private int id; + private Applet applet; + private String appletID = null; + + /* JavaScript code: + * __lc=[[JS objects],call func(index,script,appletname,isglobal)] + */ + private final static String decls = "if(!window.__lc) window.__lc=[[window],function(i,s,a,g){var v;var len=window.__lc[0].length;if(i>=len)v='E unknown object';else{var r;try{r=eval((g?'':'window.__lc[0][i]')+s);}catch(e){v='E '+e;r='E ';}finally{var t=typeof r;if(t=='undefined')v='V ';else if(t=='number')v='N '+r;else if(t=='string'){if(r!='E ')v='S '+r;}else{window.__lc[0][len]=r;v=''+len+' '+(r==window.__lc?'[array]':r);}}}a.__lc_ret=v},0]"; + + public JSObject(Applet a, String name, int _id) { + Main.info("JSObject.ctor: " + name); + jsobject = new String(name); + applet = a; + id = _id; + KJASAppletContext kc = (KJASAppletContext)applet.getAppletContext(); + appletID = kc.getAppletID(a); + if (id == 0) { + kc.evaluateJavaScript(decls, appletID, null); + } + } + + int getId() { + return id; + } + + private String escapeString(String string) { + StringBuffer sb = new StringBuffer(); + int idx = 0; + boolean cr = false; + char [] chars = string.toCharArray(); + while (idx < chars.length) { + if (cr && chars[idx] != '\n') { + cr = false; + sb.append("\\n"); + } + switch (chars[idx]) { + case '\\': + sb.append("\\\\"); + break; + case '"': + sb.append("\\\""); + break; + case '\n': + cr = false; + sb.append("\\n"); + break; + case '\r': + cr = true; + break; + default: + sb.append(chars[idx]); + } + idx++; + } + if (cr) + sb.append("\\n"); + return sb.toString(); + } + + private Object evaluate(String script, boolean global) throws netscape.javascript.JSException { + Main.info("evaluate (\"" + script + "\")"); + + KJASAppletContext kc = (KJASAppletContext) applet.getAppletContext(); + //String appletname = kc.getAppletName(appletID); + thread = Thread.currentThread(); + + if (!kc.evaluateJavaScript("window.__lc[1](" + id + ",\"" + escapeString(script) + "\",this" + (global ? ",true)" : ")"), appletID, this)) { + Main.debug("evaluate on not active applet"); + return null; + } + boolean timedout = true; + try { + Thread.sleep(30000); + } catch (InterruptedException ex) { + timedout = false; + } + thread = null; + if (timedout || returnvalue == null) + return null; + + /* lets see what we've got */ + String retval = returnvalue; + int pos = retval.indexOf(' '); + String type = retval.substring(0, pos); + if (type.equals("V")) // Void + return null; + String value = retval.substring(pos+1); + if (type.equals("E")) // Error + throw new netscape.javascript.JSException("Script error: " + value); + Main.info("value=" + value + " (type=" + type + ")"); + if (type.equals("N")) // Number + return new Double(value); + if (type.equals("S")) // String + return value; + + /* Is it an applet? */ + if (value.startsWith("[object APPLET ref=")) { + int p1 = value.indexOf('='); + int p2 = value.indexOf(']', p1+1); + int applethashcode = Integer.parseInt(value.substring(p1+1, p2)); + java.util.Enumeration e = kc.getApplets(); + while (e.hasMoreElements()) { + Applet app = (Applet) e.nextElement(); + if (app.hashCode() == applethashcode) + return app; + } + return null; + } + /* Is it a Java object then? */ + if (value.startsWith("[object ") && value.indexOf("ref=") > 0) { + int p1 = value.indexOf("ref="); + int p2 = value.indexOf(']', p1+4); + int objecthashcode = Integer.parseInt(value.substring(p1+4, p2)); + return kc.getJSReferencedObject(applet, objecthashcode); + } + /* Ok, make it a JSObject */ + return new JSObject(applet, value, Integer.parseInt(type)); + } + private String convertValueJ2JS(Object o) { + if (o == null) + return new String("null"); + if (o instanceof java.lang.Number || o instanceof java.lang.Boolean) + return o.toString(); + if (o instanceof netscape.javascript.JSObject) + return new String("window.__lc[0][" + ((JSObject)o).getId() + "]"); + return new String("\"" + escapeString(o.toString()) + "\""); + } + public Object call(String func, Object [] args) throws netscape.javascript.JSException { + Main.info("JSObject.call: " + jsobject + "." + func); + String script = new String("." + func + "("); + for (int i = 0; args != null && i < args.length; i++) + script += (i > 0 ? "," : "") + convertValueJ2JS(args[i]); + script += ")"; + return evaluate(script, false); + } + public Object eval(String s) throws netscape.javascript.JSException { + return evaluate(s, true); + } + public boolean equals(Object obj) { + Main.info("JSObject.equals"); + return super.equals(obj); + } + public Object getMember(String name) throws netscape.javascript.JSException { + Main.info("JSObject.getMember: " + jsobject + "." + name); + return evaluate("." + name, false); + } + public void setMember(String name, java.lang.Object o) throws netscape.javascript.JSException { + Main.info("JSObject.setMember: " + jsobject + "." + name); + evaluate("." + name + "=" + convertValueJ2JS(o), false); + } + public void removeMember(String name) throws netscape.javascript.JSException { + Main.info("JSObject.removeMember: " + jsobject + "." + name); + evaluate("." + name + "=null", false); + } + /* get array element; JS: this[index] */ + public Object getSlot(int index)throws netscape.javascript.JSException { + Main.info("JSObject.getSlot: " + jsobject + "[" + index + "]"); + return evaluate("[" + index + "]", false); + } + public void setSlot(int index, Object o) throws netscape.javascript.JSException { + Main.info("JSObject.setSlot: " + jsobject + "[" + index + "]"); + evaluate("[" + index + "]=" + convertValueJ2JS(o), false); + } + public String toString(){ + Main.info("JSObject.toString: " + jsobject); + return new String(jsobject); + } + public static JSObject getWindow(Applet a, int dummy) { + Main.info("JSObject.getWindow"); + return new JSObject(a, "[WINDOW]", 0); + } +} diff --git a/khtml/java/org/kde/kjas/server/Console.java b/khtml/java/org/kde/kjas/server/Console.java new file mode 100644 index 000000000..d089f3cd8 --- /dev/null +++ b/khtml/java/org/kde/kjas/server/Console.java @@ -0,0 +1,20 @@ +/* + * Appendable.java + * + * Created on 16. Mai 2002, 23:23 + */ + +package org.kde.kjas.server; + +/** + * + * @author till + */ +public interface Console { + + public void clear(); + public void append(String text); + + public void setVisible(boolean visible); + +} diff --git a/khtml/java/org/kde/kjas/server/KJASAppletClassLoader.java b/khtml/java/org/kde/kjas/server/KJASAppletClassLoader.java new file mode 100644 index 000000000..c6defa848 --- /dev/null +++ b/khtml/java/org/kde/kjas/server/KJASAppletClassLoader.java @@ -0,0 +1,360 @@ +package org.kde.kjas.server; + +import java.net.*; +import java.io.*; +import java.util.*; +import java.util.zip.*; +import java.util.jar.*; +import java.security.*; +/** + * ClassLoader used to download and instantiate Applets. + * <P> + * NOTE: The class loader extends Java 1.2 specific class. + */ +public final class KJASAppletClassLoader + extends URLClassLoader +{ + private static Hashtable loaders = new Hashtable(); + + public static synchronized void removeLoaders() + { + loaders.clear(); + } + + public static synchronized KJASAppletClassLoader getLoader( String docBase, String codeBase, String archives ) + { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkCreateClassLoader(); + } + URL docBaseURL; + KJASAppletClassLoader loader = null; + try + { + docBaseURL = new URL( docBase ); + + URL codeBaseURL = getCodeBaseURL( docBaseURL, codeBase ); + String key = codeBaseURL.toString(); + if (archives != null) + key += archives; + + Main.debug( "CL: getLoader: key = " + key ); + + loader = (KJASAppletClassLoader) loaders.get( key ); + if( loader == null ) + { + URL [] urlList = {}; + loader = new KJASAppletClassLoader( urlList, docBaseURL, codeBaseURL); + loaders.put( key, loader ); + } + else + { + Main.debug( "CL: reusing classloader" ); + } + } catch( MalformedURLException e ) { Main.kjas_err( "bad DocBase URL", e ); } + return loader; + } + + public static URL getCodeBaseURL( URL docBaseURL, String codeBase ) + { + URL codeBaseURL = null; + try + { + //first determine what the real codeBase is: 3 cases + //#1. codeBase is absolute URL- use that + //#2. codeBase is relative to docBase, create url from those + //#3. last resort, use docBase as the codeBase + if(codeBase != null) + { + //we need to do this since codeBase should be a directory + if( !codeBase.endsWith("/") ) + codeBase = codeBase + "/"; + + try + { + codeBaseURL = new URL( codeBase ); + } catch( MalformedURLException mue ) + { + try + { + codeBaseURL = new URL( docBaseURL, codeBase ); + } catch( MalformedURLException mue2 ) {} + } + } + + if(codeBaseURL == null) + { + //fall back to docBase but fix it up... + String file = docBaseURL.getFile(); + if( file == null || (file.length() == 0) ) + codeBaseURL = docBaseURL; + else if( file.endsWith( "/" ) ) + codeBaseURL = docBaseURL; + else + { + //delete up to the ending '/' + String urlString = docBaseURL.toString(); + int dot_index = urlString.lastIndexOf( '/' ); + String newfile = urlString.substring( 0, dot_index+1 ); + codeBaseURL = new URL( newfile ); + } + } + }catch( Exception e ) { Main.kjas_err( "CL: exception ", e ); } + return codeBaseURL; + } + + public static KJASAppletClassLoader getLoader( String key ) + { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkCreateClassLoader(); + } + if( loaders.containsKey( key ) ) + return (KJASAppletClassLoader) loaders.get( key ); + + return null; + } + + /********************************************************************************* + ****************** KJASAppletClassLoader Implementation ************************* + **********************************************************************************/ + private URL docBaseURL; + private URL codeBaseURL; + private Vector archives; + private String dbgID; + private static int globalId = 0; + private int myId = 0; + private Vector statusListeners = new Vector(); + private AccessControlContext acc; + // a mapping JS referenced Java objects + private Hashtable jsReferencedObjects = new Hashtable(); + final static RuntimePermission kjas_access = new RuntimePermission("accessClassInPackage.org.kde.kjas.server"); + + public KJASAppletClassLoader( URL[] urlList, URL _docBaseURL, URL _codeBaseURL) + { + super(urlList); + acc = AccessController.getContext(); + synchronized(KJASAppletClassLoader.class) { + myId = ++globalId; + } + docBaseURL = _docBaseURL; + codeBaseURL = _codeBaseURL; + archives = new Vector(); + + dbgID = "CL-" + myId + "(" + codeBaseURL.toString() + "): "; + } + + protected void addURL(URL url) { + Main.debug(this + " add URL: " + url); + super.addURL(url); + } + + public void addStatusListener(StatusListener lsnr) { + statusListeners.add(lsnr); + } + public void removeStatusListener(StatusListener lsnr) { + statusListeners.remove(lsnr); + } + public void showStatus(String msg) { + Enumeration en = statusListeners.elements(); + while (en.hasMoreElements()) { + StatusListener lsnr = (StatusListener)en.nextElement(); + lsnr.showStatus(msg); + } + } + + public void paramsDone() { + // simply builds up the search path + // put the archives first because they are + // cached. + for( int i = 0; i < archives.size(); ++i ) { + String jar = (String)archives.elementAt( i ); + try { + URL jarURL = new URL(codeBaseURL, jar); + addURL(jarURL); + Main.debug("added archive URL \"" + jarURL + "\" to KJASAppletClassLoader"); + } catch (MalformedURLException e) { + Main.kjas_err("Could not construct URL for jar file: " + codeBaseURL + " + " + jar, e); + } + } + // finally add code base url and docbase url + addURL(codeBaseURL); + + // the docBaseURL has to be fixed. + // strip file part from end otherwise this + // will be interpreted as an archive + // (should this perhaps be done generally ??) + String dbs = docBaseURL.toString(); + int idx = dbs.lastIndexOf("/"); + if (idx > 0) { + dbs = dbs.substring(0, idx+1); + } + URL docDirURL = null; + try { + docDirURL = new URL(dbs); + } catch (MalformedURLException e) { + Main.debug("Could not make a new URL from docBaseURL=" + docBaseURL); + } + if (docDirURL != null && !codeBaseURL.equals(docDirURL)) { + addURL(docDirURL); + } + } + + void addArchiveName( String jarname ) + { + if( !archives.contains( jarname ) ) + { + archives.add( jarname ); + } + } + + + public URL getDocBase() + { + return docBaseURL; + } + + public URL getCodeBase() + { + return codeBaseURL; + } + + Hashtable getJSReferencedObjects() { + return jsReferencedObjects; + } + /*************************************************************************** + **** Class Loading Methods + **************************************************************************/ + public synchronized Class findClass( String name ) throws ClassNotFoundException + { + Class rval = null; + //check the loaded classes + rval = findLoadedClass( name ); + if( rval == null ) { + try { + rval = super.findClass(name); + } catch (ClassFormatError cfe) { + Main.debug(name + ": Catched " + cfe + ". Trying to repair..."); + rval = loadFixedClass( name ); + } catch (Exception ex) { + Main.debug("findClass " + name + " " + ex.getMessage()); + } + } + if (rval == null) { + throw new ClassNotFoundException("Class: " + name); + } + return rval; + } + public Class loadClass(String name) throws ClassNotFoundException { + if (name.startsWith("org.kde.kjas.server")) { + SecurityManager sec = System.getSecurityManager(); + if (sec != null) + sec.checkPermission(kjas_access); + } + return super.loadClass(name); + } + private Hashtable loadedClasses = new Hashtable(); + + private synchronized final Class loadFixedClass(String name) throws ClassNotFoundException { + final String fileName = name.replace('.', '/') + ".class"; + try { + // try to get the class as resource + final URL u = getResource(fileName); + Main.debug(dbgID + name + ": got URL: " + u); + if (u == null) { + throw new ClassNotFoundException(fileName + ": invalid resource URL."); + } + java.security.cert.Certificate[] certs = {}; // FIXME + CodeSource cs = new CodeSource(u, certs); + InputStream instream = (InputStream)AccessController.doPrivileged( + new PrivilegedAction() { + public Object run() { + try { + return u.openStream(); + } catch (IOException ioe) { + ioe.printStackTrace(); + return null; + } + } + }, acc + ); + if (instream == null) { + throw new ClassNotFoundException(name + ": could not be loaded."); + } + ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); + int cnt; + int total = 0; + int bufSize = 1024; + byte [] buffer = new byte[bufSize]; + while ((cnt = instream.read(buffer, 0, bufSize)) > 0) { + total += cnt; + byteStream.write(buffer, 0, cnt); + } + Main.debug(dbgID + name + ": " + total + " bytes"); + + Class cl = fixAndDefineClass(name, byteStream.toByteArray(), 0, total, cs); + if (cl != null) { + loadedClasses.put(name, cl); + } + return cl; + + } catch (Throwable e) { + e.printStackTrace(); + throw new ClassNotFoundException("triggered by " + e); + } + } + + public URL findResource( String name) + { + Main.debug( dbgID + "findResource, name = " + name ); + String displayName = name; + try { + URL u = new URL(name); + String filename = u.getFile(); + if (filename != null && filename.length() > 0) { + displayName = filename; + } + } catch (Throwable e) { + } + showStatus("Loading: " + displayName); + URL url = super.findResource( name ); + Main.debug("findResource for " + name + " returns " + url); + return url; + } + + protected PermissionCollection getPermissions(CodeSource cs) { + Main.debug(dbgID + " getPermissions(" + cs + ")"); + PermissionCollection permissions = super.getPermissions(cs); + Enumeration perms_enum = permissions.elements(); + while (perms_enum.hasMoreElements()) { + Main.debug(this + " Permission: " + perms_enum.nextElement()); + } + return permissions; + } + + + /** + * define the class <b>name</b>. If <b>name</b> is broken, try to fix it. + */ + private final Class fixAndDefineClass( + String name, + byte[] b, + int off, + int len, + CodeSource cs) throws ClassFormatError + { + KJASBrokenClassFixer fixer = new KJASBrokenClassFixer(); + if (fixer.process(b, off, len)) { + Main.debug(name + " fixed"); + } else { + Main.info(name + " could not be fixed"); + } + return defineClass(name, + fixer.getProcessedData(), + fixer.getProcessedDataOffset(), + fixer.getProcessedDataLength(), + cs); + } + + +} diff --git a/khtml/java/org/kde/kjas/server/KJASAppletContext.java b/khtml/java/org/kde/kjas/server/KJASAppletContext.java new file mode 100644 index 000000000..f868b7b64 --- /dev/null +++ b/khtml/java/org/kde/kjas/server/KJASAppletContext.java @@ -0,0 +1,473 @@ +package org.kde.kjas.server; + +import java.applet.*; +import java.util.*; +import java.net.*; +import java.awt.*; +import java.awt.event.*; +import java.io.*; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import org.kde.javascript.JSObject; + +final class KJASAuthenticator extends Authenticator { + private Hashtable authentication; + + KJASAuthenticator() { + authentication = new Hashtable(); + setDefault(this); + } + final void addURL(URL url, String user, String password, String authname) { + String key = new String(url.getProtocol() + ":" + url.getHost() + ":" + + url.getPort() + "_" + authname); + String [] auths = { user, password }; + authentication.put(key, auths); + } + final protected PasswordAuthentication getPasswordAuthentication() { + URL url; + String key = new String(getRequestingProtocol() + ":" + getRequestingHost() + ":" + getRequestingPort() + "_" + getRequestingPrompt()); + String [] auths = (String []) authentication.get(key); + if (auths != null) { + char [] pw = new char[auths[1].length()]; + auths[1].getChars(0, auths[1].length(), pw, 0); + return new PasswordAuthentication(auths[0], pw); + } + return null; + } +} + +/** + * The context in which applets live. + */ +public class KJASAppletContext implements AppletContext +{ + private Hashtable stubs; + private Hashtable images; + private Vector pendingImages; + private Hashtable streams; + private Stack jsobjects; + + private String myID; + private KJASAppletClassLoader loader; + private boolean active; + private final static KJASAuthenticator authenticator = new KJASAuthenticator(); + + /** + * Create a KJASAppletContext + */ + public KJASAppletContext( String _contextID ) + { + stubs = new Hashtable(); + images = new Hashtable(); + pendingImages = new Vector(); + streams = new Hashtable(); + jsobjects = new Stack(); + myID = _contextID; + active = true; + } + + public String getID() + { + return myID; + } + + public String getAppletID(Applet applet) + { + Enumeration e = stubs.keys(); + while ( e.hasMoreElements() ) + { + String appletID = (String) e.nextElement(); + KJASAppletStub stub = (KJASAppletStub) stubs.get(appletID); + if (stub.getApplet() == applet) + return appletID; + } + return null; + } + + public Applet getAppletById(String appletId) { + return ((KJASAppletStub) stubs.get( appletId )).getApplet(); + } + + public String getAppletName(String appletID) { + KJASAppletStub stub = (KJASAppletStub) stubs.get(appletID); + if (stub == null) + return null; + return stub.getAppletName(); + } + public void createApplet( String appletID, String name, + String className, String docBase, + String username, String password, String authname, + String codeBase, String archives, + String width, String height, + String windowName, Hashtable params ) + { + //do kludges to support mess with parameter table and + //the applet variables + String key = new String( "ARCHIVE" ); + if (params.containsKey(key)) { + String param_archive = (String)params.get(key); + if (archives == null) { + // There is no 'archive' attribute + // but a 'archive' param. fix archive list + // from param value + archives = param_archive; + } else { + // there is already an archive attribute. + // just add the value of the param to the list. + // But ignore bill$ personal archive format called + // .cab because java doesn't understand it. + if (!param_archive.toLowerCase().endsWith(".cab")) { + archives = param_archive + "," + archives; + } + } + } else if (archives != null) { + // add param if it is not present + params.put( key, archives); + } + + if( codeBase == null ) + { + key = new String( "CODEBASE" ); + if( params.containsKey( key ) ) + codeBase = (String) params.get( key ); + } + + if (username != null && !username.equals("")) { + try { + URL url = new URL(docBase); + int port = url.getPort(); + if (port < 0) + port = url.getDefaultPort(); + authenticator.addURL(new URL(url.getProtocol(), url.getHost(), port, ""), username, password, authname); + } catch (MalformedURLException muex) { + } + } + try + { + String sorted_archives = ""; + TreeSet archive_set = new TreeSet(); + if( archives != null ) + { + StringTokenizer parser = new StringTokenizer( archives, ",", false ); + while( parser.hasMoreTokens() ) + archive_set.add ( parser.nextToken().trim() ); + } + Iterator it = archive_set.iterator(); + while (it.hasNext()) + sorted_archives += (String) it.next(); + KJASAppletClassLoader loader = + KJASAppletClassLoader.getLoader( docBase, codeBase, sorted_archives ); + it = archive_set.iterator(); + while (it.hasNext()) + loader.addArchiveName( (String) it.next() ); + loader.paramsDone(); + + KJASAppletStub stub = new KJASAppletStub + ( + this, appletID, loader.getCodeBase(), + loader.getDocBase(), name, className, + new Dimension( Integer.parseInt(width), Integer.parseInt(height) ), + params, windowName, loader + ); + stubs.put( appletID, stub ); + + stub.createApplet(); + } + catch ( Exception e ) + { + Main.kjas_err( "Something bad happened in createApplet: " + e, e ); + } + } + + public void initApplet( String appletID ) + { + KJASAppletStub stub = (KJASAppletStub) stubs.get( appletID ); + if( stub == null ) + { + Main.debug( "could not init and show applet: " + appletID ); + } + else + { + stub.initApplet(); + } + } + + public void destroyApplet( String appletID ) + { + KJASAppletStub stub = (KJASAppletStub) stubs.get( appletID ); + + if( stub == null ) + { + Main.debug( "could not destroy applet: " + appletID ); + } + else + { + //Main.debug( "stopping applet: " + appletID ); + stubs.remove( appletID ); + + stub.destroyApplet(); + } + } + + public void startApplet( String appletID ) + { + KJASAppletStub stub = (KJASAppletStub) stubs.get( appletID ); + if( stub == null ) + { + Main.debug( "could not start applet: " + appletID ); + } + else + { + stub.startApplet(); + } + } + + public void stopApplet( String appletID ) + { + KJASAppletStub stub = (KJASAppletStub) stubs.get( appletID ); + if( stub == null ) + { + Main.debug( "could not stop applet: " + appletID ); + } + else + { + stub.stopApplet(); + } + } + + public void destroy() + { + Enumeration e = stubs.elements(); + while ( e.hasMoreElements() ) + { + KJASAppletStub stub = (KJASAppletStub) e.nextElement(); + stub.destroyApplet(); + stub.loader.getJSReferencedObjects().clear(); + } + + stubs.clear(); + active = false; + } + + /*************************************************************************** + **** AppletContext interface + ***************************************************************************/ + public Applet getApplet( String appletName ) + { + if( active ) + { + Enumeration e = stubs.elements(); + while( e.hasMoreElements() ) + { + KJASAppletStub stub = (KJASAppletStub) e.nextElement(); + + if( stub.getAppletName().equals( appletName ) ) + return stub.getApplet(); + } + } + + return null; + } + + public Enumeration getApplets() + { + if( active ) + { + Vector v = new Vector(); + Enumeration e = stubs.elements(); + while( e.hasMoreElements() ) + { + KJASAppletStub stub = (KJASAppletStub) e.nextElement(); + v.add( stub.getApplet() ); + } + + return v.elements(); + } + + return null; + } + + public AudioClip getAudioClip( URL url ) + { + Main.debug( "getAudioClip, url = " + url ); + //AudioClip clip = java.applet.Applet.newAudioClip(url); + AudioClip clip = new KJASAudioClip(url); + Main.debug( "got AudioClip " + clip); + return clip; + // return new KJASSoundPlayer( myID, url ); + } + + public void addImage( String url, byte[] data ) + { + Main.debug( "addImage for url = " + url ); + images.put( url, data ); + if (Main.cacheImages) { + pendingImages.remove(url); + } + } + + public Image getImage( URL url ) + { + if( active && url != null ) + { + // directly load images using JVM + if (true) { + // Main.info("Getting image using ClassLoader:" + url); + if (loader != null) { + url = loader.findResource(url.toString()); + //Main.debug("Resulting URL:" + url); + } + Toolkit kit = Toolkit.getDefaultToolkit(); + Image img = kit.createImage(url); + return img; + } + + //check with the Web Server + String str_url = url.toString(); + Main.debug( "getImage, url = " + str_url ); + if (Main.cacheImages && images.containsKey(str_url)) { + Main.debug("Cached: url=" + str_url); + } + else + { + if (Main.cacheImages) { + if (!pendingImages.contains(str_url)) { + Main.protocol.sendGetURLDataCmd( myID, str_url ); + pendingImages.add(str_url); + } + } else { + Main.protocol.sendGetURLDataCmd( myID, str_url ); + } + while( !images.containsKey( str_url ) && active ) + { + try { Thread.sleep( 200 ); } + catch( InterruptedException e ){} + } + } + if( images.containsKey( str_url ) ) + { + byte[] data = (byte[]) images.get( str_url ); + if( data.length > 0 ) + { + Toolkit kit = Toolkit.getDefaultToolkit(); + return kit.createImage( data ); + } else return null; + } + } + + return null; + } + + public void showDocument( URL url ) + { + //Main.debug( "showDocument, url = " + url ); + + if( active && (url != null) ) + { + Main.protocol.sendShowDocumentCmd( myID, url.toString() ); + } + } + + public void showDocument( URL url, String targetFrame ) + { + //Main.debug( "showDocument, url = " + url + " targetFrame = " + targetFrame ); + + if( active && (url != null) && (targetFrame != null) ) + { + Main.protocol.sendShowDocumentCmd( myID, url.toString(), targetFrame ); + } + } + + public void showStatus( String message ) + { + if( active && (message != null) ) + { + Main.protocol.sendShowStatusCmd( myID, message ); + } + } + public boolean evaluateJavaScript(String script, String appletID, JSObject jso) { + KJASAppletStub stub = (KJASAppletStub) stubs.get( appletID ); + if( active && stub != null && stub.isLoaded ()) { + if( jso != null ) { + synchronized (jsobjects) { + jsobjects.push(jso); + } + } + int [] types = { KJASAppletStub.JString }; + String [] arglist = { script }; + Main.protocol.sendJavaScriptEventCmd(myID, appletID, 0, "eval", types, arglist); + return true; + } + Main.debug( "evaluateJavaScript failure, context active:" + active + " stub:" + stub); + return false; + } + + public boolean getMember(String appletID, int callid, int objid, String name) + { + KJASAppletStub stub = (KJASAppletStub) stubs.get( appletID ); + if (stub == null || !stub.isLoaded()) + return false; + return stub.getMember(callid, objid, name); + } + + public boolean putMember(String appletID, int callid, int objid, String name, String value) + { + if (name.equals("__lc_ret")) { + // special case; return value of JS script evaluation + Main.debug("putValue: applet " + name + "=" + value); + JSObject jso = null; + synchronized (jsobjects) { + if (!jsobjects.empty()) + jso = (JSObject) jsobjects.pop(); + } + if (jso == null) + return false; + jso.returnvalue = value; + try { + jso.thread.interrupt(); + } catch (SecurityException ex) {} + Main.protocol.sendPutMember( myID, callid, true ); + } + KJASAppletStub stub = (KJASAppletStub) stubs.get( appletID ); + if (stub == null || !stub.isLoaded()) + return false; + return stub.putMember(callid, objid, name, value); + } + + public Object getJSReferencedObject(Applet applet, int objid) + { + return ((KJASAppletClassLoader)(applet.getClass().getClassLoader())).getJSReferencedObjects().get(new Integer(objid)); + } + boolean callMember(String appletID, int cid, int oid, String n, java.util.List args) + { + KJASAppletStub stub = (KJASAppletStub) stubs.get( appletID ); + if (stub == null || !stub.isLoaded()) + return false; + return stub.callMember( cid, oid, n, args); + } + public void derefObject(String appletID, int objid) { + if (objid == 0) + return; // that's an applet + KJASAppletStub stub = (KJASAppletStub) stubs.get( appletID ); + if (stub == null) + return; + Hashtable jsRefs = stub.loader.getJSReferencedObjects(); + if (jsRefs.remove(new Integer(objid)) == null) + Main.debug("couldn't remove referenced object"); + } + + public void setStream(String key, InputStream stream) throws IOException { + Main.debug("setStream, key = " + key); + streams.put(key, stream); + } + public InputStream getStream(String key){ + Main.debug("getStream, key = " + key); + return (InputStream) streams.get(key); + } + public Iterator getStreamKeys() { + Main.debug("getStreamKeys"); + return streams.keySet().iterator(); + } + + +} diff --git a/khtml/java/org/kde/kjas/server/KJASAppletPanel.java b/khtml/java/org/kde/kjas/server/KJASAppletPanel.java new file mode 100644 index 000000000..d7acbdaf9 --- /dev/null +++ b/khtml/java/org/kde/kjas/server/KJASAppletPanel.java @@ -0,0 +1,113 @@ +package org.kde.kjas.server; + +import java.applet.Applet; +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.LayoutManager; +import java.awt.Panel; +import java.net.URL; + +/** + * @author till + * + * A panel which embeds the applet and shows some + * information during class loading. + */ +public class KJASAppletPanel extends javax.swing.JPanel implements StatusListener { + private final static int LOADING = 1; + private final static int RUNNING = 2; + private final static int FAILED = 3; + + private Image load_img = null; + private Image fail_img = null; + private int status = LOADING; + private Font font; + private String msg = "Loading Applet..."; + + /** + * Constructor for KJASAppletPanel. + */ + public KJASAppletPanel() { + super(new BorderLayout()); + font = new Font("SansSerif", Font.PLAIN, 10); + URL url = + getClass().getClassLoader().getResource("images/animbean.gif"); + load_img = getToolkit().createImage(url); + //setBackground(Color.white); + } + + void setApplet(Applet applet) { + add("Center", applet); + validate(); + } + + public void showStatus(String msg) { + this.msg = msg; + if (status != RUNNING) + repaint(); + } + + public void paint(Graphics g) { + super.paint(g); + if (status == RUNNING) + return; + Image img = (status == LOADING ? load_img : fail_img); + int x = getWidth() / 2; + int y = getHeight() / 2; + if (img != null) { + //synchronized (img) { + int w = img.getWidth(this); + int h = img.getHeight(this); + int imgx = x - w / 2; + int imgy = y - h / 2; + //g.setClip(imgx, imgy, w, h); + g.drawImage(img, imgx, imgy, this); + y += img.getHeight(this) / 2; + //} + } + if (msg != null) { + //synchronized(msg) { + g.setFont(font); + FontMetrics m = g.getFontMetrics(); + int h = m.getHeight(); + int w = m.stringWidth(msg); + int msgx = x - w / 2; + int msgy = y + h; + //g.setClip(0, y, getWidth(), h); + g.drawString(msg, msgx, msgy); + //} + } + } + void showFailed() { + URL url = + getClass().getClassLoader().getResource("images/brokenbean.gif"); + fail_img = getToolkit().createImage(url); + status = FAILED; + msg = "Applet Failed."; + repaint(); + } + + void showFailed(String message) { + showFailed(); + showStatus(message); + } + + public void stopAnimation() { + status = RUNNING; + } + + public boolean imageUpdate(Image img, int flags, int x, int y, int w, int h) + { + if (img != null && img == load_img && status != LOADING) { + img.flush(); + load_img = null; + Main.debug("flushing image"); + return false; + } + return super.imageUpdate(img, flags, x, y, w, h); + } +} diff --git a/khtml/java/org/kde/kjas/server/KJASAppletStub.java b/khtml/java/org/kde/kjas/server/KJASAppletStub.java new file mode 100644 index 000000000..e090183d7 --- /dev/null +++ b/khtml/java/org/kde/kjas/server/KJASAppletStub.java @@ -0,0 +1,807 @@ +package org.kde.kjas.server; + +import java.applet.*; +import java.util.*; +import java.net.*; +import java.awt.*; +import java.awt.event.*; +import javax.swing.JFrame; +import java.security.PrivilegedAction; +import java.security.AccessController; +import java.security.AccessControlContext; +import java.security.ProtectionDomain; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +/** + * The stub used by Applets to communicate with their environment. + * + */ +public final class KJASAppletStub + implements AppletStub +{ + private KJASAppletContext context; // The containing context. + private Hashtable params; // Maps parameter names to values + private URL codeBase; // The URL directory where files are + private URL docBase; // The document that referenced the applet + private boolean active; // Is the applet active? + private String appletName; // The name of this applet instance + private String appletID; // The id of this applet- for use in callbacks + private Dimension appletSize; + private String windowName; + private String className; + private Class appletClass; + private JFrame frame; + + /** + * out of bounds applet state :-), perform an action + */ + public static final int ACTION = -1; + /** + * applet state unknown + */ + public static final int UNKNOWN = 0; + /** + * the applet class has been loaded + */ + public static final int CLASS_LOADED = 1; + /** + * the applet has been instanciated + */ + public static final int INSTANCIATED = 2; + /** + * the applet has been initialized + */ + public static final int INITIALIZED = 3; + /** + * the applet has been started + */ + public static final int STARTED = 4; + /** + * the applet has been stopped + */ + public static final int STOPPED = 5; + /** + * the applet has been destroyed + */ + public static final int DESTROYED = 6; + /** + * request for termination of the applet thread + */ + private static final int TERMINATE = 7; + /** + * like TERMINATE, an end-point state + */ + private static final int FAILED = 8; + + + //private KJASAppletClassLoader loader; + KJASAppletClassLoader loader; + private KJASAppletPanel panel; + private Applet app; + KJASAppletStub me; + + /** + * Interface for so called LiveConnect actions, put-, get- and callMember + */ + // keep this in sync with KParts::LiveConnectExtension::Type + private final static int JError = -1; + private final static int JVoid = 0; + private final static int JBoolean = 1; + private final static int JFunction = 2; + private final static int JNumber = 3; + private final static int JObject = 4; + final static int JString = 5; + + interface AppletAction { + void apply(); + void fail(); + } + + private class RunThread extends Thread { + private int request_state = CLASS_LOADED; + private int current_state = UNKNOWN; + private Vector actions = new Vector(); + private AccessControlContext acc = null; + + RunThread() { + super("KJAS-AppletStub-" + appletID + "-" + appletName); + setContextClassLoader(loader); + } + /** + * Ask applet to go to the next state + */ + synchronized void requestState(int nstate) { + if (nstate > current_state) { + request_state = nstate; + notifyAll(); + } + } + synchronized void requestAction(AppletAction action) { + actions.add(action); + notifyAll(); + } + /** + * Get the asked state + */ + synchronized private int getRequestState() { + while (request_state == current_state) { + if (!actions.isEmpty()) { + if (current_state >= INITIALIZED && current_state < STOPPED) + return ACTION; + else { + AppletAction action = (AppletAction) actions.remove(0); + action.fail(); + } + } else { + try { + wait (); + } catch(InterruptedException ie) { + } + } + } + if (request_state == DESTROYED && current_state == STARTED) + return current_state + 1; // make sure we don't skip stop() + return request_state; + } + /** + * Get the current state + */ + synchronized int getAppletState() { + return current_state; + } + /** + * Set the current state + */ + synchronized private void setState(int nstate) { + current_state = nstate; + } + /** + * Put applet in asked state + * Note, kjavaapletviewer asks for create/start/stop/destroy, the + * missing states instance/init/terminate, we do automatically + */ + private void doState(int nstate) throws ClassNotFoundException, IllegalAccessException, InstantiationException { + switch (nstate) { + case CLASS_LOADED: + appletClass = loader.loadClass( className ); + requestState(INSTANCIATED); + break; + case INSTANCIATED: { + Object object = null; + try { + object = appletClass.newInstance(); + app = (Applet) object; + } + catch ( ClassCastException e ) { + if ( object != null && object instanceof java.awt.Component) { + app = new Applet(); + app.setLayout(new BorderLayout()); + app.add( (Component) object, BorderLayout.CENTER); + } else + throw e; + } + acc = new AccessControlContext(new ProtectionDomain[] {app.getClass().getProtectionDomain()}); + requestState(INITIALIZED); + break; + } + case INITIALIZED: + app.setStub( me ); + app.setVisible(false); + panel.setApplet( app ); + if (appletSize.getWidth() > 0) + app.setBounds( 0, 0, appletSize.width, appletSize.height ); + else + app.setBounds( 0, 0, panel.getSize().width, panel.getSize().height ); + app.init(); + loader.removeStatusListener(panel); + // stop the loading... animation + panel.stopAnimation(); + app.setVisible(true); + break; + case STARTED: + active = true; + app.start(); + frame.validate(); + app.repaint(); + break; + case STOPPED: + active = false; + app.stop(); + if (Main.java_version > 1.399) { + // kill the windowClosing listener(s) + WindowListener[] l = frame.getWindowListeners(); + for (int i = 0; l != null && i < l.length; i++) + frame.removeWindowListener(l[i]); + } + frame.setVisible(false); + break; + case DESTROYED: + if (app != null) + app.destroy(); + frame.dispose(); + app = null; + requestState(TERMINATE); + break; + default: + return; + } + } + /** + * RunThread run(), loop until state is TERMINATE + */ + public void run() { + while (true) { + int nstate = getRequestState(); + if (nstate >= TERMINATE) + return; + if (nstate == ACTION) { + AccessController.doPrivileged( + new PrivilegedAction() { + public Object run() { + AppletAction action = (AppletAction) actions.remove(0); + try { + action.apply(); + } catch (Exception ex) { + Main.debug("Error during action " + ex); + action.fail(); + } + return null; + } + }, + acc); + } else { // move to nstate + try { + doState(nstate); + } catch (Exception ex) { + Main.kjas_err("Error during state " + nstate, ex); + if (nstate < INITIALIZED) { + setState(FAILED); + setFailed(ex.toString()); + return; + } + } catch (Throwable tr) { + setState(FAILED); + setFailed(tr.toString()); + return; + } + setState(nstate); + stateChange(nstate); + } + } + } + } + private RunThread runThread = null; + + /** + * Create an AppletStub for the specified applet. The stub will be in + * the specified context and will automatically attach itself to the + * passed applet. + */ + public KJASAppletStub( KJASAppletContext _context, String _appletID, + URL _codeBase, URL _docBase, + String _appletName, String _className, + Dimension _appletSize, Hashtable _params, + String _windowName, KJASAppletClassLoader _loader ) + { + context = _context; + appletID = _appletID; + codeBase = _codeBase; + docBase = _docBase; + active = false; + appletName = _appletName; + className = _className.replace( '/', '.' ); + appletSize = _appletSize; + params = _params; + windowName = _windowName; + loader = _loader; + + String fixedClassName = _className; + if (_className.endsWith(".class") || _className.endsWith(".CLASS")) + { + fixedClassName = _className.substring(0, _className.length()-6); + } + else if (_className.endsWith(".java")|| _className.endsWith(".JAVA")) + { + fixedClassName = _className.substring(0, _className.length()-5); + } + className = fixedClassName.replace('/', '.'); + + appletClass = null; + me = this; + + + } + + private void stateChange(int newState) { + Main.protocol.sendAppletStateNotification( + context.getID(), + appletID, + newState); + } + + private void setFailed(String why) { + loader.removeStatusListener(panel); + panel.stopAnimation(); + panel.showFailed(); + Main.protocol.sendAppletFailed(context.getID(), appletID, why); + } + + void createApplet() { + panel = new KJASAppletPanel(); + frame = new JFrame(windowName); + // under certain circumstances, it may happen that the + // applet is not embedded but shown in a separate window. + // think of konqueror running under fvwm or gnome. + // than, the user should have the ability to close the window. + + frame.addWindowListener + ( + new WindowAdapter() { + public void windowClosing(WindowEvent e) { + me.destroyApplet(); + } + } + ); + frame.getContentPane().add( panel, BorderLayout.CENTER ); + try { + if (Main.java_version > 1.399) + frame.setUndecorated(true); + } catch(java.awt.IllegalComponentStateException e) { + // This happens with gcj 4.0.1, ignore for now... + } + frame.setLocation( 0, 0 ); + frame.pack(); + // resize frame for j2sdk1.5beta1.. + if (appletSize.getWidth() > 0) + frame.setBounds( 0, 0, appletSize.width, appletSize.height ); + else + frame.setBounds( 0, 0, 50, 50 ); + frame.setVisible(true); + loader.addStatusListener(panel); + runThread = new RunThread(); + runThread.start(); + } + + /** + * starts the applet managed by this stub by calling the applets start() method. + * Also marks this stub as active. + * @see java.applet.Applet#start() + * @see java.applet.AppletStub#isActive() + * + */ + void startApplet() + { + runThread.requestState(STARTED); + } + + /** + * stops the applet managed by this stub by calling the applets stop() method. + * Also marks this stub as inactive. + * @see java.applet.Applet#stop() + * @see java.applet.AppletStub#isActive() + * + */ + void stopApplet() + { + runThread.requestState(STOPPED); + } + + /** + * initialize the applet managed by this stub by calling the applets init() method. + * @see java.applet.Applet#init() + */ + void initApplet() + { + runThread.requestState(INITIALIZED); + } + + /** + * destroys the applet managed by this stub by calling the applets destroy() method. + * Also marks the the applet as inactive. + * @see java.applet.Applet#init() + */ + synchronized void destroyApplet() + { + runThread.requestState(DESTROYED); + } + + static void waitForAppletThreads() + { + Thread [] ts = new Thread[Thread.activeCount() + 5]; + int len = Thread.enumerate(ts); + for (int i = 0; i < len; i++) { + try { + if (ts[i].getName() != null && + ts[i].getName().startsWith("KJAS-AppletStub-")) { + try { + ((RunThread) ts[i]).requestState(TERMINATE); + ts[i].join(10000); + } catch (InterruptedException ie) {} + } + } catch (Exception e) {} + } + } + + /** + * get the Applet managed by this stub. + * @return the Applet or null if the applet could not be loaded + * or instanciated. + */ + Applet getApplet() + { + if (runThread != null && runThread.getAppletState() > CLASS_LOADED) + return app; + return null; + } + + /** + * get a parameter value given in the <APPLET> tag + * @param name the name of the parameter + * @return the value or null if no parameter with this name exists. + */ + + public String getParameter( String name ) + { + return (String) params.get( name.toUpperCase() ); + } + + /** + * implements the isActive method of the AppletStub interface. + * @return if the applet managed by this stub is currently active. + * @see java.applet.AppletStub#isActive() + */ + public boolean isActive() + { + return active; + } + + /** + * determines if the applet has been loaded and instanciated + * and can hence be used. + * @return true if the applet has been completely loaded. + */ + boolean isLoaded() { + if (runThread == null) + return false; + int state = runThread.getAppletState(); + return (state >= INSTANCIATED && state < DESTROYED); + } + + public void appletResize( int width, int height ) + { + if( active ) + { + if ( (width >= 0) && (height >= 0)) + { + Main.debug( "Applet #" + appletID + ": appletResize to : (" + width + ", " + height + ")" ); + Main.protocol.sendResizeAppletCmd( context.getID(), appletID, width, height ); + appletSize = new Dimension( width, height ); + //pack(); + } + } + } + + /** + * converts Object <b>arg</b> into an object of class <b>cl</b>. + * @param arg Object to convert + * @param cl Destination class + * @return An Object of the specified class with the value specified + * in <b>arg</b> + */ + private static final Object cast(Object arg, Class cl) throws NumberFormatException { + Object ret = arg; + if (arg == null) { + ret = null; + } + else if (cl.isAssignableFrom(arg.getClass())) { + return arg; + } + else if (arg instanceof String) { + String s = (String)arg; + Main.debug("Argument String: \"" + s + "\""); + if (cl == Boolean.TYPE || cl == Boolean.class) { + ret = new Boolean(s); + } else if (cl == Integer.TYPE || cl == Integer.class) { + ret = new Integer(s); + } else if (cl == Long.TYPE || cl == Long.class) { + ret = new Long(s); + } else if (cl == Float.TYPE || cl == Float.class) { + ret = new Float(s); + } else if (cl == Double.TYPE || cl == Double.class) { + ret = new Double(s); + } else if (cl == Short.TYPE || cl == Short.class) { + ret = new Short(s); + } else if (cl == Byte.TYPE || cl == Byte.class) { + ret = new Byte(s); + } else if (cl == Character.TYPE || cl == Character.class) { + ret = new Character(s.charAt(0)); + } + } + return ret; + } + private Method findMethod(Class c, String name, Class [] argcls) { + try { + Method[] methods = c.getMethods(); + for (int i = 0; i < methods.length; i++) { + Method m = methods[i]; + if (m.getName().equals(name)) { + Main.debug("Candidate: " + m); + Class [] parameterTypes = m.getParameterTypes(); + if (argcls == null) { + if (parameterTypes.length == 0) { + return m; + } + } else { + if (argcls.length == parameterTypes.length) { + for (int j = 0; j < argcls.length; j++) { + // Main.debug("Parameter " + j + " " + parameterTypes[j]); + argcls[j] = parameterTypes[j]; + } + return m; + } + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + private int[] getJSTypeValue(Hashtable jsRefs, Object obj, int objid, StringBuffer value) { + String val = obj.toString(); + int[] rettype = { JError, objid }; + String type = obj.getClass().getName(); + if (type.equals("boolean") || type.equals("java.lang.Boolean")) + rettype[0] = JBoolean; + else if (type.equals("int") || type.equals("long") || + type.equals("float") || type.equals("double") || + type.equals("byte") || obj instanceof java.lang.Number) + rettype[0] = JNumber; + else if (type.equals("java.lang.String")) + rettype[0] = JString; + else if (!type.startsWith("org.kde.kjas.server") && + !(obj instanceof java.lang.Class && + ((Class)obj).getName().startsWith("org.kde.kjas.server"))) { + rettype[0] = JObject; + rettype[1] = obj.hashCode(); + jsRefs.put(new Integer(rettype[1]), obj); + } + value.insert(0, val); + return rettype; + } + private class PutAction implements AppletAction { + int call_id; + int objid; + String name; + String value; + PutAction(int cid, int oid, String n, String v) { + call_id = cid; + objid = oid; + name = n; + value = v; + } + public void apply() { + Hashtable jsRefs = loader.getJSReferencedObjects(); + Object o = objid==0 ? getApplet() : jsRefs.get(new Integer(objid)); + if (o == null) { + Main.debug("Error in putValue: object " + objid + " not found"); + fail(); + return; + } + Field f; + try { + f = o.getClass().getField(name); + } catch (Exception e) { + fail(); + return; + } + if (f == null) { + Main.debug("Error in putValue: " + name + " not found"); + fail(); + return; + } + try { + String type = f.getType().getName(); + Main.debug("putValue: (" + type + ")" + name + "=" + value); + if (type.equals("boolean")) + f.setBoolean(o, Boolean.getBoolean(value)); + else if (type.equals("java.lang.Boolean")) + f.set(o, Boolean.valueOf(value)); + else if (type.equals("int")) + f.setInt(o, Integer.parseInt(value)); + else if (type.equals("java.lang.Integer")) + f.set(o, Integer.valueOf(value)); + else if (type.equals("byte")) + f.setByte(o, Byte.parseByte(value)); + else if (type.equals("java.lang.Byte")) + f.set(o, Byte.valueOf(value)); + else if (type.equals("char")) + f.setChar(o, value.charAt(0)); + else if (type.equals("java.lang.Character")) + f.set(o, new Character(value.charAt(0))); + else if (type.equals("double")) + f.setDouble(o, Double.parseDouble(value)); + else if (type.equals("java.lang.Double")) + f.set(o, Double.valueOf(value)); + else if (type.equals("float")) + f.setFloat(o, Float.parseFloat(value)); + else if (type.equals("java.lang.Float")) + f.set(o, Float.valueOf(value)); + else if (type.equals("long")) + f.setLong(o, Long.parseLong(value)); + else if (type.equals("java.lang.Long")) + f.set(o, Long.valueOf(value)); + else if (type.equals("short")) + f.setShort(o, Short.parseShort(value)); + else if (type.equals("java.lang.Short")) + f.set(o, Short.valueOf(value)); + else if (type.equals("java.lang.String")) + f.set(o, value); + else { + Main.debug("Error putValue: unsupported type: " + type); + fail(); + return; + } + } catch (Exception e) { + Main.debug("Exception in putValue: " + e.getMessage()); + fail(); + return; + } + Main.protocol.sendPutMember( context.getID(), call_id, true ); + } + public void fail() { + Main.protocol.sendPutMember( context.getID(), call_id, false ); + } + } + private class GetAction implements AppletAction { + int call_id; + int objid; + String name; + GetAction(int cid, int oid, String n) { + call_id = cid; + objid = oid; + name = n; + } + public void apply() { + Main.debug("getMember: " + name); + StringBuffer value = new StringBuffer(); + int ret[] = { JError, objid }; + Hashtable jsRefs = loader.getJSReferencedObjects(); + Object o = objid==0 ? getApplet() : jsRefs.get(new Integer(objid)); + if (o == null) { + fail(); + return; + } + Class c = o.getClass(); + try { + Field field = c.getField(name); + ret = getJSTypeValue(jsRefs, field.get(o), objid, value); + } catch (Exception ex) { + Method [] m = c.getMethods(); + for (int i = 0; i < m.length; i++) + if (m[i].getName().equals(name)) { + ret[0] = JFunction; + break; + } + } + Main.protocol.sendMemberValue(context.getID(), KJASProtocolHandler.GetMember, call_id, ret[0], ret[1], value.toString()); + } + public void fail() { + Main.protocol.sendMemberValue(context.getID(), KJASProtocolHandler.GetMember, call_id, -1, 0, ""); + } + } + private class CallAction implements AppletAction { + int call_id; + int objid; + String name; + java.util.List args; + CallAction(int cid, int oid, String n, java.util.List a) { + call_id = cid; + objid = oid; + name = n; + args = a; + } + public void apply() { + StringBuffer value = new StringBuffer(); + Hashtable jsRefs = loader.getJSReferencedObjects(); + int [] ret = { JError, objid }; + Object o = objid==0 ? getApplet() : jsRefs.get(new Integer(objid)); + if (o == null) { + fail(); + return; + } + + try { + Main.debug("callMember: " + name); + Object obj; + Class c = o.getClass(); + String type; + Class [] argcls = new Class[args.size()]; + for (int i = 0; i < args.size(); i++) + argcls[i] = name.getClass(); // String for now, will be updated by findMethod + Method m = findMethod(c, (String) name, argcls); + Main.debug("Found Method: " + m); + if (m != null) { + Object [] argobj = new Object[args.size()]; + for (int i = 0; i < args.size(); i++) { + argobj[i] = cast(args.get(i), argcls[i]); + } + Object retval = m.invoke(o, argobj); + if (retval == null) + ret[0] = JVoid; + else + ret = getJSTypeValue(jsRefs, retval, objid, value); + } + } catch (Exception e) { + Main.debug("callMember threw exception: " + e.toString()); + } + Main.protocol.sendMemberValue(context.getID(), KJASProtocolHandler.CallMember, call_id, ret[0], ret[1], value.toString()); + } + public void fail() { + Main.protocol.sendMemberValue(context.getID(), KJASProtocolHandler.CallMember, call_id, -1, 0, ""); + } + } + boolean putMember(int callid, int objid, String name, String val) { + if (runThread == null) + return false; + runThread.requestAction( new PutAction( callid, objid, name, val) ); + return true; + } + boolean getMember(int cid, int oid, String name) { + if (runThread == null) + return false; + runThread.requestAction( new GetAction( cid, oid, name) ); + return true; + } + boolean callMember(int cid, int oid, String name, java.util.List args) { + if (runThread == null) + return false; + runThread.requestAction( new CallAction( cid, oid, name, args) ); + return true; + } + /************************************************************************* + ********************** AppletStub Interface ***************************** + *************************************************************************/ + /** + * implements the getAppletContext method of the AppletStub interface. + * @return the AppletContext to which this stub belongs. + * @see java.applet.AppletStub#getAppletContext() + */ + public AppletContext getAppletContext() + { + return context; + } + + /** + * implements the getCodeBase method of the AppletStub interface. + * @return the code base of the applet as given in the <APPLET> tag. + * @see java.applet.AppletStub#getCodeBase() + */ + public URL getCodeBase() + { + return codeBase; + } + + /** + * implements the getDocumentBase method of the AppletStub interface. + * @return the code base of the applet as given in the + * <APPLET> tag or determined by the containing page. + * @see java.applet.AppletStub#getDocumentBase() + */ + public URL getDocumentBase() + { + return docBase; + } + + /** + * get the applet's name + * @return the name of the applet as given in the + * <APPLET> tag or determined by the <em>code</em> parameter. + */ + public String getAppletName() + { + return appletName; + } + +} diff --git a/khtml/java/org/kde/kjas/server/KJASAudioClip.java b/khtml/java/org/kde/kjas/server/KJASAudioClip.java new file mode 100644 index 000000000..3a40cf6e0 --- /dev/null +++ b/khtml/java/org/kde/kjas/server/KJASAudioClip.java @@ -0,0 +1,98 @@ +package org.kde.kjas.server; + +import java.applet.*; +import java.net.*; +import java.util.*; +/** +* Background Audioclip Loader and Player. +* @author Till Krech (till@snafu.de) +*/ +public class KJASAudioClip implements AudioClip +{ + private AudioClip theClip; + private final static int PLAYING = 1; + private final static int LOOPING = 2; + private final static int STOPPED = 3; + private int state; + private static Hashtable cache = new Hashtable(); + + /** + * creates a new Audioclip. + * The AudioClip is loaded in background. The Constructor returns immediately. + */ + public KJASAudioClip(URL url) + { + state = STOPPED; + theClip = (AudioClip)cache.get(url); + if (theClip == null) { + final URL theUrl = url; + + new Thread + ( + new Runnable() { + public void run() { + theClip = java.applet.Applet.newAudioClip(theUrl); + cache.put(theUrl, theClip); + if (state == LOOPING) { + theClip.loop(); + } else if (state == PLAYING) { + theClip.play(); + } + } + }, "AudioClipLoader " + url.getFile() + ).start(); + } + } + + /** + * play continously when the clip is loaded + */ + public void loop() + { + state = LOOPING; + if (theClip != null) { + new Thread + ( + new Runnable() { + public void run() { + theClip.loop(); + } + }, "AudioClipLooper " + ).start(); + } + } + + /** + * play when the clip is loaded + */ + public void play() + { + state = PLAYING; + if (theClip != null) { + new Thread + ( + new Runnable() { + public void run() { + theClip.play(); + } + }, "AudioClipPlayer " + ).start(); + } + } + + /** + * stop the clip + */ + public void stop() + { + state = STOPPED; + if (theClip != null) { + theClip.stop(); + } + } + + public void finalize() { + stop(); + } +} + diff --git a/khtml/java/org/kde/kjas/server/KJASBrokenClassFixer.java b/khtml/java/org/kde/kjas/server/KJASBrokenClassFixer.java new file mode 100644 index 000000000..aab1be4af --- /dev/null +++ b/khtml/java/org/kde/kjas/server/KJASBrokenClassFixer.java @@ -0,0 +1,132 @@ +package org.kde.kjas.server; +import java.lang.reflect.*; +import java.net.URLClassLoader; +import java.net.URL; +/** +* wrapper for the javaplugin.jar Broken11ClassFixer. +* Uses the reflection api to wrap the class <i>sun.plugin.security.Broken11ClassFixer</i> +* from the javaplugin.jar archive which can be found in the jre/lib directory. +*/ +public class KJASBrokenClassFixer { + private static Class fixerClass = null; + private static Method _process; + private static Method _getProcessedData; + private static Method _getProcessedDataOffset; + private static Method _getProcessedDataLength; + private static boolean initialized = false; + private static final String fixerClassName = "sun.plugin.security.Broken11ClassFixer"; + private Object fixer = null; + private byte [] bytes; + private int offset; + private int length; + + /** + * creates a new KJASBrokenClassFixer. + * If it is the first one to be created, it tries to load the class + * <i>sun.plugin.security.Broken11ClassFixer</i> from the jar file + * <i>lib/javaplugin.jar</i> in the java jre directory. + */ + public KJASBrokenClassFixer() { + init(); + if (fixerClass != null) { + try { + fixer = fixerClass.newInstance(); + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + + /** + * loads the class <i>sun.plugin.security.Broken11ClassFixer</i>, + * initializes the methods, ... + */ + private synchronized void init() { + if (initialized) { + return; + } + try { + URL [] urls = { new URL( + "file", "", 0, + System.getProperty("java.home") + + System.getProperty("file.separator") + + "lib" + + System.getProperty("file.separator") + + "javaplugin.jar"), new URL( + "file", "", 0, + System.getProperty("java.home") + + System.getProperty("file.separator") + + "lib" + + System.getProperty("file.separator") + + "plugin.jar") + }; + URLClassLoader loader = new URLClassLoader(urls); + fixerClass = Class.forName(fixerClassName, true, loader); + Main.debug("Loaded " + fixerClass); + final Class [] parameterTypes = { + (new byte[1]).getClass(), + Integer.TYPE, + Integer.TYPE + }; + final Class [] noParameter = new Class[0]; + _process = fixerClass.getMethod("process", parameterTypes); + _getProcessedData = fixerClass.getMethod("getProcessedData", noParameter); + _getProcessedDataOffset = fixerClass.getMethod("getProcessedDataOffset", noParameter); + _getProcessedDataLength = fixerClass.getMethod("getProcessedDataLength", noParameter); + } catch (Throwable e) { + e.printStackTrace(); + } finally { + initialized = true; + } + } + /** + * scan the broken bytes and create new ones. + * If the wrapped class could not be loaded or + * no instance of Broken11ClassFixer could be instantiated, + * this is a noop and later calls to getProcessedData() etc. + * will return the original data passed as arguments in this + * call. + */ + public boolean process(byte [] b, int off, int len) { + if (fixer != null) { + try { + Object [] args = new Object[3]; + args[0] = b; + args[1] = new Integer(off); + args[2] = new Integer(len); + Object [] none = new Object[0]; + + _process.invoke(fixer, args); + this.bytes = (byte[])_getProcessedData.invoke(fixer, none); + this.offset = ((Integer)_getProcessedDataOffset.invoke(fixer, none)).intValue(); + this.length = ((Integer)_getProcessedDataLength.invoke(fixer, none)).intValue(); + return true; + } catch (Throwable e) { + } + } + this.bytes = b; + this.offset = off; + this.length = len; + return false; + } + + /** + * get the offset in the processed byte array + */ + public int getProcessedDataOffset() { + return offset; + } + /** + * get the length of the processed data + */ + public int getProcessedDataLength() { + return length; + } + /** + * get the processed (fixed) data + */ + public byte [] getProcessedData() { + return bytes; + } + +} diff --git a/khtml/java/org/kde/kjas/server/KJASConsole.java b/khtml/java/org/kde/kjas/server/KJASConsole.java new file mode 100644 index 000000000..51498b59b --- /dev/null +++ b/khtml/java/org/kde/kjas/server/KJASConsole.java @@ -0,0 +1,93 @@ +package org.kde.kjas.server; + +import java.awt.*; +import java.awt.event.*; +import java.io.*; + +public class KJASConsole + extends Frame + implements Console +{ + private TextArea txt; + + public KJASConsole() + { + super("Konqueror Java Console"); + + txt = new TextArea(); + txt.setEditable(false); + txt.setBackground(Color.white); + txt.setForeground(Color.black); + + Panel main = new Panel(new BorderLayout()); + Panel btns = new Panel(new BorderLayout()); + + Button clear = new Button("Clear"); + Button close = new Button("Close"); + + btns.add(clear, "West"); + btns.add(close, "East"); + + main.add(txt, "Center"); + main.add(btns, "South"); + + add( main ); + + clear.addActionListener + ( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + txt.setText(""); + } + } + ); + + close.addActionListener + ( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + setVisible(false); + } + } + ); + + addWindowListener + ( + new WindowAdapter() { + public void windowClosing(WindowEvent e) { + setVisible(false); + } + } + ); + + setSize(500, 300); + + PrintStream st = new PrintStream( new KJASConsoleStream(this) ); + System.setOut(st); + System.setErr(st); + + System.out.println( "Java VM version: " + + System.getProperty("java.version") ); + System.out.println( "Java VM vendor: " + + System.getProperty("java.vendor") ); + } + + public void clear() { + txt.setText(""); + } + + public void append(String msg) { + if (msg == null) { + return; + } + int length = msg.length(); + synchronized(txt) { + //get the caret position, and then get the new position + int old_pos = txt.getCaretPosition(); + txt.append(msg); + txt.setCaretPosition( old_pos + length ); + } + } +} + + diff --git a/khtml/java/org/kde/kjas/server/KJASConsoleStream.java b/khtml/java/org/kde/kjas/server/KJASConsoleStream.java new file mode 100644 index 000000000..2c1152ed4 --- /dev/null +++ b/khtml/java/org/kde/kjas/server/KJASConsoleStream.java @@ -0,0 +1,46 @@ +package org.kde.kjas.server; +import java.io.*; + +class KJASConsoleStream + extends OutputStream +{ + private Console console; + private FileOutputStream dbg_log; + + public KJASConsoleStream(Console console) + { + this.console = console; + + try + { + if( Main.log ) + { + dbg_log = new FileOutputStream( "/tmp/kjas.log"); + } + } + catch( FileNotFoundException e ) {} + } + + public void close() {} + public void flush() {} + public void write(byte[] b) {} + public void write(int a) {} + + // Should be enough for the console + public void write( byte[] bytes, int offset, int length ) + { + try // Just in case + { + String msg = new String( bytes, offset, length ); + console.append(msg); + if( Main.log && dbg_log != null ) + { + dbg_log.write( msg.getBytes() ); + dbg_log.flush(); + } + } + catch(Throwable t) {} + } +} + + diff --git a/khtml/java/org/kde/kjas/server/KJASProtocolHandler.java b/khtml/java/org/kde/kjas/server/KJASProtocolHandler.java new file mode 100644 index 000000000..f8b50a91d --- /dev/null +++ b/khtml/java/org/kde/kjas/server/KJASProtocolHandler.java @@ -0,0 +1,900 @@ +package org.kde.kjas.server; + +import java.io.*; +import java.util.*; +import java.awt.*; +import java.net.*; + +/** + * Encapsulates the KJAS protocol and manages the contexts + * + */ +public class KJASProtocolHandler +{ + // Command codes- always need to be synced up with + // what's in kjavaappletserver.cpp + private static final int CreateContextCode = 1; + private static final int DestroyContextCode = 2; + private static final int CreateAppletCode = 3; + private static final int DestroyAppletCode = 4; + private static final int StartAppletCode = 5; + private static final int StopAppletCode = 6; + private static final int InitAppletCode = 7; + private static final int ShowDocumentCode = 8; + private static final int ShowURLInFrameCode = 9; + private static final int ShowStatusCode = 10; + private static final int ResizeAppletCode = 11; + private static final int GetURLDataCode = 12; + private static final int URLDataCode = 13; + private static final int ShutdownServerCode = 14; + private static final int JavaScriptEvent = 15; + static final int GetMember = 16; + static final int CallMember = 17; + private static final int PutMember = 18; + private static final int DerefObject = 19; + + private static final int AudioClipPlayCode = 20; + private static final int AudioClipLoopCode = 21; + private static final int AudioClipStopCode = 22; + + private static final int AppletStateNotificationCode = 23; + private static final int AppletFailedCode = 24; + private static final int DataCommand = 25; + private static final int PutURLDataCode = 26; + private static final int PutDataCode = 27; + private static final int SecurityConfirmCode = 28; + private static final int ShowConsole = 29; + + //Holds contexts in contextID-context pairs + private Hashtable contexts; + + private PushbackInputStream commands; //Stream for reading in commands + private PrintStream signals; //Stream for writing out callbacks + + //used for parsing each command as it comes in + private int cmd_index; + private final static char sep = (char) 0; + + public KJASProtocolHandler( InputStream _commands, + OutputStream _signals ) + { + commands = new PushbackInputStream( _commands ); + signals = new PrintStream( _signals ); + contexts = new Hashtable(); + } + + public void commandLoop() + { + try + { + while( true ) + { + try + { + int cmd_length = readPaddedLength( 8 ); + Main.debug( "PH: cmd_length = " + cmd_length ); + + //We need to have this while loop since we're not guaranteed to get + //all the bytes we want back, especially with large jars + byte[] cmd_data = new byte[cmd_length]; + int total_read = 0; + while( total_read < cmd_length ) + { + int numread = commands.read( cmd_data, total_read, cmd_length-total_read ); + Main.debug( "PH: read in " + numread + " bytes for command" ); + total_read += numread; + } + + //parse the rest of the command and execute it + processCommand( cmd_data ); + } + catch( NumberFormatException e ) + { + Main.kjas_err( "Could not parse out message length", e ); + e.printStackTrace(); + System.exit( 1 ); + } + catch( Throwable t ) + { + Main.debug( "commandLoop caught a throwable, still going" ); + t.printStackTrace(); + } + } + } + catch( Exception i ) + { + Main.kjas_err( "commandLoop exited on exception: ", i ); + i.printStackTrace(); + System.exit( 1 ); + } + } + + public void processCommand( byte[] command ) + { + // Sanity checks + if ( command == null ) + return; + + //do all the parsing here and pass arguments as individual variables to the + //handler functions + int cmd_length = command.length; + cmd_index = 0; + + int cmd_code_value = (int) command[cmd_index++]; + if( cmd_code_value == CreateContextCode ) + { + //parse out contextID- 1 argument + String contextID = getArg( command ); + Main.debug( "createContext, id = " + contextID ); + + KJASAppletContext context = new KJASAppletContext( contextID ); + contexts.put( contextID, context ); + } else + if( cmd_code_value == DestroyContextCode ) + { + //parse out contextID- 1 argument + String contextID = getArg( command ); + Main.debug( "destroyContext, id = " + contextID ); + + KJASAppletContext context = (KJASAppletContext) contexts.get( contextID ); + if( contexts != null ) + { + context.destroy(); + contexts.remove( contextID ); + } + } else + if( cmd_code_value == CreateAppletCode ) + { + //9 arguments- this order is important... + final String contextID = getArg( command ); + final String appletID = getArg( command ); + final String appletName = getArg( command ); + final String className = getArg( command ); + final String baseURL = getArg( command ); + final String username = getArg( command ); + final String password = getArg( command ); + final String authname = getArg( command ); + final String codeBase = getArg( command ); + final String archives = getArg( command ); + final String width = getArg( command ); + final String height = getArg( command ); + final String title = getArg( command ); + + //get the number of parameter pairs... + String str_params = getArg( command ); + int num_params = Integer.parseInt( str_params.trim() ); + final Hashtable params = new Hashtable(); + for( int i = 0; i < num_params; i++ ) + { + String name = getArg( command ); // note name is in uppercase + if( name == null ) + name = new String(); + + String value = getArg( command ); + if( value == null ) + value = new String(); + params.put( name, value ); + //Main.debug( "parameter, name = " + name + ", value = " + value ); + } + + Main.debug( "createApplet, context = " + contextID + ", applet = " + appletID ); + Main.debug( " name = " + appletName + ", classname = " + className ); + Main.debug( " baseURL = " + baseURL + ", codeBase = " + codeBase ); + Main.debug( " archives = " + archives + ", width = " + width + + ", height = " + height ); + + final KJASAppletContext context = (KJASAppletContext) contexts.get( contextID ); + if( context != null ) + { + context.createApplet( appletID, appletName, className, + baseURL, username, password, authname, + codeBase, archives, + width, height, title, params ); + } + + } else + if( cmd_code_value == DestroyAppletCode ) + { + //2 arguments + String contextID = getArg( command ); + String appletID = getArg( command ); + Main.debug( "destroyApplet, context = " + contextID + ", applet = " + appletID ); + + KJASAppletContext context = (KJASAppletContext) contexts.get( contextID ); + if ( context != null ) + context.destroyApplet( appletID ); + } else + if( cmd_code_value == StartAppletCode ) + { + //2 arguments + String contextID = getArg( command ); + String appletID = getArg( command ); + Main.debug( "startApplet, context = " + contextID + ", applet = " + appletID ); + + KJASAppletContext context = (KJASAppletContext) contexts.get( contextID ); + if ( context != null ) + context.startApplet( appletID ); + } else + if( cmd_code_value == StopAppletCode ) + { + //2 arguments + String contextID = getArg( command ); + String appletID = getArg( command ); + Main.debug( "stopApplet, context = " + contextID + ", applet = " + appletID ); + + KJASAppletContext context = (KJASAppletContext) contexts.get( contextID ); + if ( context != null ) + context.stopApplet( appletID ); + } else + if( cmd_code_value == ShutdownServerCode ) + { + Main.debug( "shutDownServer received" ); + KJASAppletStub.waitForAppletThreads(); + System.exit( 1 ); + } + else + if( cmd_code_value == URLDataCode ) + { + + String id = getArg( command ); + String code = getArg( command ); + Main.debug( "KIO URLData received(" + id + ") code:" + code ); + + //rest of the command should be the data... + byte[] data = null; + if (cmd_length - cmd_index > 0) { + data = new byte[ cmd_length - cmd_index ]; + System.arraycopy( command, cmd_index, data, 0, data.length ); + } + KIOConnection.setData(id, Integer.parseInt(code), data); + } else + if (cmd_code_value == GetMember) + { + int ticketnr = Integer.parseInt( getArg( command ) ); + String contextID = getArg( command ); + String appletID = getArg( command ); + int objid = Integer.parseInt( getArg( command ) ); + String name = getArg( command ); + KJASAppletContext context = (KJASAppletContext) contexts.get( contextID ); + if ( context == null || !context.getMember(appletID, ticketnr, objid, name)) + sendMemberValue(contextID, GetMember, ticketnr, -1, 0, ""); + } else + if (cmd_code_value == PutMember) + { + int ticketnr = Integer.parseInt( getArg( command ) ); + String contextID = getArg( command ); + String appletID = getArg( command ); + int objid = Integer.parseInt( getArg( command ) ); + String name = getArg( command ); + String value = getArg( command ); + boolean ret = false; + KJASAppletContext context = (KJASAppletContext) contexts.get( contextID ); + if (context == null || !context.putMember(appletID, ticketnr, objid, name, value)) + sendPutMember(contextID, ticketnr, false); + } else + if (cmd_code_value == CallMember) + { + int ticketnr = Integer.parseInt( getArg( command ) ); + String contextID = getArg( command ); + String appletID = getArg( command ); + int objid = Integer.parseInt( getArg( command ) ); + String name = getArg( command ); + int arg_count = Integer.parseInt( getArg( command ) ); + java.util.List args = new java.util.Vector(); + try { // fix getArg + String param = getArg(command); + while (arg_count-- > 0) { + if (param == null) + param = new String(); + args.add(param); + param = getArg(command); + } + } catch (Exception e) {} + + KJASAppletContext context = (KJASAppletContext) contexts.get( contextID ); + if ( context == null || !context.callMember(appletID, ticketnr, objid, name, args)) + Main.protocol.sendMemberValue(contextID, CallMember, ticketnr, -1, 0, ""); + } else + if (cmd_code_value == DerefObject) + { + String contextID = getArg( command ); + String appletID = getArg( command ); + String objid = getArg( command ); + KJASAppletContext context = (KJASAppletContext) contexts.get( contextID ); + if ( context != null ) + context.derefObject(appletID, Integer.parseInt(objid)); + Main.debug( "DerefObject " + objid); + } else + if (cmd_code_value == SecurityConfirmCode) + { + String id = getArg( command ); + String confirm = getArg( command ); + Thread t = (Thread) KJASSecurityManager.confirmRequests.get(id); + Main.debug( "SecurityConfirmCode " + id + " confirm:" + confirm ); + if (t != null) { + KJASSecurityManager.confirmRequests.put(id, confirm); + try { + t.interrupt(); + } catch (SecurityException se) {} + } + } else + if (cmd_code_value == ShowConsole) + { + Main.console.setVisible(true); + } + else + { + throw new IllegalArgumentException( "Unknown command code" ); + } + } + + /************************************************************** + ***** Methods for talking to the applet server ************** + **************************************************************/ + + /** + * sends get url request + */ + public void sendGetURLDataCmd( String jobid, String url ) + { + Main.debug( "sendGetURLCmd(" + jobid + ") url = " + url ); + //length = length of args plus 1 for code, 2 for seps and 1 for end + byte [] url_bytes = url.getBytes(); + int length = jobid.length() + url_bytes.length + 4; + byte [] bytes = new byte[ length + 8 ]; + byte [] tmp_bytes = getPaddedLengthBytes( length ); + int index = 0; + + System.arraycopy( tmp_bytes, 0, bytes, index, tmp_bytes.length ); + index += tmp_bytes.length; + bytes[index++] = (byte) GetURLDataCode; + bytes[index++] = sep; + + tmp_bytes = jobid.getBytes(); + System.arraycopy( tmp_bytes, 0, bytes, index, tmp_bytes.length ); + index += tmp_bytes.length; + bytes[index++] = sep; + + System.arraycopy( url_bytes, 0, bytes, index, url_bytes.length ); + index += url_bytes.length; + bytes[index++] = sep; + + signals.write( bytes, 0, bytes.length ); + } + + /** + * sends command for get url request (stop/hold/resume) or put (stop) + */ + public void sendDataCmd( String id, int cmd ) + { + Main.debug( "sendDataCmd(" + id + ") command = " + cmd ); + byte [] cmd_bytes = String.valueOf( cmd ).getBytes(); + int length = id.length() + cmd_bytes.length + 4; + byte [] bytes = new byte[ length + 8 ]; + byte [] tmp_bytes = getPaddedLengthBytes( length ); + int index = 0; + + System.arraycopy( tmp_bytes, 0, bytes, index, tmp_bytes.length ); + index += tmp_bytes.length; + bytes[index++] = (byte) DataCommand; + bytes[index++] = sep; + + tmp_bytes = id.getBytes(); + System.arraycopy( tmp_bytes, 0, bytes, index, tmp_bytes.length ); + index += tmp_bytes.length; + bytes[index++] = sep; + + System.arraycopy( cmd_bytes, 0, bytes, index, cmd_bytes.length ); + index += cmd_bytes.length; + bytes[index++] = sep; + + signals.write( bytes, 0, bytes.length ); + } + /** + * sends put url request + */ + public void sendPutURLDataCmd( String jobid, String url ) + { + Main.debug( "sendPutURLCmd(" + jobid + ") url = " + url ); + //length = length of args plus 1 for code, 2 for seps and 1 for end + byte [] url_bytes = url.getBytes(); + int length = jobid.length() + url_bytes.length + 4; + byte [] bytes = new byte[ length + 8 ]; + byte [] tmp_bytes = getPaddedLengthBytes( length ); + int index = 0; + + System.arraycopy( tmp_bytes, 0, bytes, index, tmp_bytes.length ); + index += tmp_bytes.length; + bytes[index++] = (byte) PutURLDataCode; + bytes[index++] = sep; + + tmp_bytes = jobid.getBytes(); + System.arraycopy( tmp_bytes, 0, bytes, index, tmp_bytes.length ); + index += tmp_bytes.length; + bytes[index++] = sep; + + System.arraycopy( url_bytes, 0, bytes, index, url_bytes.length ); + index += url_bytes.length; + bytes[index++] = sep; + + signals.write( bytes, 0, bytes.length ); + } + /** + * sends put data + */ + public void sendPutData( String jobid, byte [] b, int off, int len ) + { + Main.debug( "sendPutData(" + jobid + ") len = " + len ); + //length = length of args plus 1 for code, 2 for seps and 1 for end + int length = jobid.length() + len + 4; + byte [] bytes = new byte[ length + 8 ]; + byte [] tmp_bytes = getPaddedLengthBytes( length ); + int index = 0; + + System.arraycopy( tmp_bytes, 0, bytes, index, tmp_bytes.length ); + index += tmp_bytes.length; + bytes[index++] = (byte) PutDataCode; + bytes[index++] = sep; + + tmp_bytes = jobid.getBytes(); + System.arraycopy( tmp_bytes, 0, bytes, index, tmp_bytes.length ); + index += tmp_bytes.length; + bytes[index++] = sep; + + System.arraycopy( b, off, bytes, index, len ); + index += len; + bytes[index++] = sep; + + signals.write( bytes, 0, bytes.length ); + } + /** + * sends notification about the state of the applet. + * @see org.kde.kjas.server.KJASAppletStub for valid states + */ + public void sendAppletStateNotification( String contextID, String appletID, int state ) + { + Main.debug( "sendAppletStateNotification, contextID = " + contextID + ", appletID = " + + appletID + ", state=" + state ); + + byte [] state_bytes = String.valueOf( state ).getBytes(); + + int length = contextID.length() + appletID.length() + state_bytes.length + 5; + byte [] bytes = new byte[ length + 8 ]; //for length of message + byte [] tmp_bytes = getPaddedLengthBytes( length ); + int index = 0; + + System.arraycopy( tmp_bytes, 0, bytes, index, tmp_bytes.length ); + index += tmp_bytes.length; + bytes[index++] = (byte) AppletStateNotificationCode; + bytes[index++] = sep; + + tmp_bytes = contextID.getBytes(); + System.arraycopy( tmp_bytes, 0, bytes, index, tmp_bytes.length ); + index += tmp_bytes.length; + bytes[index++] = sep; + + tmp_bytes = appletID.getBytes(); + System.arraycopy( tmp_bytes, 0, bytes, index, tmp_bytes.length ); + index += tmp_bytes.length; + bytes[index++] = sep; + + System.arraycopy( state_bytes, 0, bytes, index, state_bytes.length ); + index += state_bytes.length; + bytes[index++] = sep; + + signals.write( bytes, 0, bytes.length ); + } + + /** + * sends notification about applet failure. + * This can happen in any state. + * @param contextID context ID of the applet's context + * @param appletID ID of the applet + * @param errorMessage any message + */ + public void sendAppletFailed ( String contextID, String appletID, String errorMessage) + { + Main.debug( "sendAppletFailed, contextID = " + contextID + ", appletID = " + + appletID + ", errorMessage=" + errorMessage ); + byte [] msg_bytes = errorMessage.getBytes(); + int length = contextID.length() + appletID.length() + msg_bytes.length + 5; + byte [] bytes = new byte[ length + 8 ]; //for length of message + byte [] tmp_bytes = getPaddedLengthBytes( length ); + int index = 0; + + System.arraycopy( tmp_bytes, 0, bytes, index, tmp_bytes.length ); + index += tmp_bytes.length; + bytes[index++] = (byte) AppletFailedCode; + bytes[index++] = sep; + + tmp_bytes = contextID.getBytes(); + System.arraycopy( tmp_bytes, 0, bytes, index, tmp_bytes.length ); + index += tmp_bytes.length; + bytes[index++] = sep; + + tmp_bytes = appletID.getBytes(); + System.arraycopy( tmp_bytes, 0, bytes, index, tmp_bytes.length ); + index += tmp_bytes.length; + bytes[index++] = sep; + + System.arraycopy( msg_bytes, 0, bytes, index, msg_bytes.length ); + index += msg_bytes.length; + bytes[index++] = sep; + + signals.write( bytes, 0, bytes.length ); + } + + public void sendShowDocumentCmd( String loaderKey, String url ) + { + Main.debug( "sendShowDocumentCmd from context#" + loaderKey + " url = " + url ); + + //length = length of args + 2 for seps + 1 for end + 1 for code + byte [] url_bytes = url.getBytes(); + byte [] key_bytes = loaderKey.getBytes(); + int length = key_bytes.length + url_bytes.length + 4; + byte [] bytes = new byte[ length + 8 ]; //8 for the length of this message + byte [] tmp_bytes = getPaddedLengthBytes( length ); + int index = 0; + + System.arraycopy( tmp_bytes, 0, bytes, index, tmp_bytes.length ); + index += tmp_bytes.length; + bytes[index++] = (byte) ShowDocumentCode; + bytes[index++] = sep; + + System.arraycopy( key_bytes, 0, bytes, index, key_bytes.length ); + index += key_bytes.length; + bytes[index++] = sep; + + System.arraycopy( url_bytes, 0, bytes, index, url_bytes.length ); + index += url_bytes.length; + bytes[index++] = sep; + + signals.write( bytes, 0, bytes.length ); + } + + public void sendShowDocumentCmd( String contextID, String url, String frame) + { + Main.debug( "sendShowDocumentCmd from context#" + contextID + + " url = " + url + ", frame = " + frame ); + + //length = length of args plus code, 3 seps, end + byte [] url_bytes = url.getBytes(); + byte [] frame_bytes = frame.getBytes(); + int length = contextID.length() + url_bytes.length + frame_bytes.length + 5; + byte [] bytes = new byte[ length + 8 ]; //for length of message + byte [] tmp_bytes = getPaddedLengthBytes( length ); + int index = 0; + + System.arraycopy( tmp_bytes, 0, bytes, index, tmp_bytes.length ); + index += tmp_bytes.length; + bytes[index++] = (byte) ShowURLInFrameCode; + bytes[index++] = sep; + + tmp_bytes = contextID.getBytes(); + System.arraycopy( tmp_bytes, 0, bytes, index, tmp_bytes.length ); + index += tmp_bytes.length; + bytes[index++] = sep; + + System.arraycopy( url_bytes, 0, bytes, index, url_bytes.length ); + index += url_bytes.length; + bytes[index++] = sep; + + System.arraycopy( frame_bytes, 0, bytes, index, frame_bytes.length ); + index += frame_bytes.length; + bytes[index++] = sep; + + signals.write( bytes, 0, bytes.length ); + } + + public void sendShowStatusCmd( String contextID, String msg ) + { + Main.debug( "sendShowStatusCmd, contextID = " + contextID + " msg = " + msg ); + + byte [] msg_bytes = msg.getBytes(); + int length = contextID.length() + msg_bytes.length + 4; + byte [] bytes = new byte[ length + 8 ]; //for length of message + int index = 0; + + byte [] tmp_bytes = getPaddedLengthBytes( length ); + System.arraycopy( tmp_bytes, 0, bytes, index, tmp_bytes.length ); + index += tmp_bytes.length; + bytes[index++] = (byte) ShowStatusCode; + bytes[index++] = sep; + + tmp_bytes = contextID.getBytes(); + System.arraycopy( tmp_bytes, 0, bytes, index, tmp_bytes.length ); + index += tmp_bytes.length; + bytes[index++] = sep; + + System.arraycopy( msg_bytes, 0, bytes, index, msg_bytes.length ); + index += msg_bytes.length; + bytes[index++] = sep; + + signals.write( bytes, 0, bytes.length ); + } + + public void sendResizeAppletCmd( String contextID, String appletID, + int width, int height ) + { + Main.debug( "sendResizeAppletCmd, contextID = " + contextID + ", appletID = " + + appletID + ", width = " + width + ", height = " + height ); + + byte [] width_bytes = String.valueOf( width ).getBytes(); + byte [] height_bytes = String.valueOf( height ).getBytes(); + + //length = length of args plus code, 4 seps, end + int length = contextID.length() + appletID.length() + width_bytes.length + + height_bytes.length + 6; + byte [] bytes = new byte[ length + 8 ]; //for length of message + byte [] tmp_bytes = getPaddedLengthBytes( length ); + int index = 0; + + System.arraycopy( tmp_bytes, 0, bytes, index, tmp_bytes.length ); + index += tmp_bytes.length; + bytes[index++] = (byte) ResizeAppletCode; + bytes[index++] = sep; + + tmp_bytes = contextID.getBytes(); + System.arraycopy( tmp_bytes, 0, bytes, index, tmp_bytes.length ); + index += tmp_bytes.length; + bytes[index++] = sep; + + tmp_bytes = appletID.getBytes(); + System.arraycopy( tmp_bytes, 0, bytes, index, tmp_bytes.length ); + index += tmp_bytes.length; + bytes[index++] = sep; + + System.arraycopy( width_bytes, 0, bytes, index, width_bytes.length ); + index += width_bytes.length; + bytes[index++] = sep; + + System.arraycopy( height_bytes, 0, bytes, index, height_bytes.length ); + index += height_bytes.length; + bytes[index++] = sep; + + signals.write( bytes, 0, bytes.length ); + } + public void sendJavaScriptEventCmd( String contextID, String appletID, int objid, String event, int [] types, String [] args ) + { + Main.debug( "sendJavaScriptEventCmd, contextID = " + contextID + " event = " + event ); + String objstr = new String("" + objid); + int length = contextID.length() + appletID.length() + event.length() + objstr.length() + 6; + byte [][][] arglist = null; + if (types != null) { + arglist = new byte[args.length][2][]; + for (int i = 0; i < types.length; i++) { + arglist[i][0] = (new String("" + types[i])).getBytes(); + arglist[i][1] = args[i].getBytes(); + length += 2 + arglist[i][0].length + arglist[i][1].length; + } + } + byte [] bytes = new byte[ length + 8 ]; //for length of message + int index = 0; + + byte [] tmp_bytes = getPaddedLengthBytes( length ); + System.arraycopy( tmp_bytes, 0, bytes, index, tmp_bytes.length ); + index += tmp_bytes.length; + bytes[index++] = (byte) JavaScriptEvent; + bytes[index++] = sep; + + tmp_bytes = contextID.getBytes(); + System.arraycopy( tmp_bytes, 0, bytes, index, tmp_bytes.length ); + index += tmp_bytes.length; + bytes[index++] = sep; + + tmp_bytes = appletID.getBytes(); + System.arraycopy( tmp_bytes, 0, bytes, index, tmp_bytes.length ); + index += tmp_bytes.length; + bytes[index++] = sep; + + tmp_bytes = objstr.getBytes(); + System.arraycopy( tmp_bytes, 0, bytes, index, tmp_bytes.length ); + index += tmp_bytes.length; + bytes[index++] = sep; + + tmp_bytes = event.getBytes(); + System.arraycopy( tmp_bytes, 0, bytes, index, tmp_bytes.length ); + index += tmp_bytes.length; + bytes[index++] = sep; + + if (types != null) + for (int i = 0; i < types.length; i++) { + System.arraycopy( arglist[i][0], 0, bytes, index, arglist[i][0].length ); + index += arglist[i][0].length; + bytes[index++] = sep; + System.arraycopy( arglist[i][1], 0, bytes, index, arglist[i][1].length ); + index += arglist[i][1].length; + bytes[index++] = sep; + } + + signals.write( bytes, 0, bytes.length ); + } + public void sendMemberValue( String contextID, int cmd, int ticketnr, int type, int rid, String value ) + { + Main.debug( "sendMemberValue, contextID = " + contextID + " value = " + value + " type=" + type + " rid=" + rid ); + + String strticket = String.valueOf( ticketnr ); + String strtype = String.valueOf( type ); + String strobj = String.valueOf( rid ); + byte [] value_bytes = value.getBytes(); + int length = contextID.length() + value_bytes.length + strtype.length() + strobj.length() + strticket.length() + 7; + byte [] bytes = new byte[ length + 8 ]; //for length of message + int index = 0; + + byte [] tmp_bytes = getPaddedLengthBytes( length ); + System.arraycopy( tmp_bytes, 0, bytes, index, tmp_bytes.length ); + index += tmp_bytes.length; + bytes[index++] = (byte) cmd; + bytes[index++] = sep; + + tmp_bytes = contextID.getBytes(); + System.arraycopy( tmp_bytes, 0, bytes, index, tmp_bytes.length ); + index += tmp_bytes.length; + bytes[index++] = sep; + + tmp_bytes = strticket.getBytes(); + System.arraycopy( tmp_bytes, 0, bytes, index, tmp_bytes.length ); + index += tmp_bytes.length; + bytes[index++] = sep; + + tmp_bytes = strtype.getBytes(); + System.arraycopy( tmp_bytes, 0, bytes, index, tmp_bytes.length ); + index += tmp_bytes.length; + bytes[index++] = sep; + + tmp_bytes = strobj.getBytes(); + System.arraycopy( tmp_bytes, 0, bytes, index, tmp_bytes.length ); + index += tmp_bytes.length; + bytes[index++] = sep; + + System.arraycopy( value_bytes, 0, bytes, index, value_bytes.length ); + index += value_bytes.length; + bytes[index++] = sep; + + signals.write( bytes, 0, bytes.length ); + } + + private void sendAudioClipCommand(String contextId, String url, int cmd) { + byte [] url_bytes = url.getBytes(); + int length = contextId.length() + url_bytes.length + 4; + byte [] bytes = new byte[ length + 8 ]; + byte [] tmp_bytes = getPaddedLengthBytes( length ); + int index = 0; + + System.arraycopy( tmp_bytes, 0, bytes, index, tmp_bytes.length ); + index += tmp_bytes.length; + bytes[index++] = (byte) cmd; + bytes[index++] = sep; + + tmp_bytes = contextId.getBytes(); + System.arraycopy( tmp_bytes, 0, bytes, index, tmp_bytes.length ); + index += tmp_bytes.length; + bytes[index++] = sep; + + System.arraycopy( url_bytes, 0, bytes, index, url_bytes.length ); + index += url_bytes.length; + bytes[index++] = sep; + + signals.write( bytes, 0, bytes.length ); + } + + public void sendAudioClipPlayCommand(String contextId, String url) { + sendAudioClipCommand(contextId, url, AudioClipPlayCode); + } + public void sendAudioClipLoopCommand(String contextId, String url) { + sendAudioClipCommand(contextId, url, AudioClipLoopCode); + } + public void sendAudioClipStopCommand(String contextId, String url) { + sendAudioClipCommand(contextId, url, AudioClipStopCode); + } + + public void sendPutMember( String contextID, int ticketnr, boolean success ) + { + Main.debug("sendPutMember, contextID = " + contextID + " success = " + success); + + byte [] ticket_bytes = String.valueOf( ticketnr ).getBytes(); + byte [] ret_bytes = String.valueOf( success ? "1" : "0" ).getBytes(); + int length = contextID.length() + ret_bytes.length + ticket_bytes.length + 5; + byte [] bytes = new byte[ length + 8 ]; //for length of message + byte [] tmp_bytes = getPaddedLengthBytes( length ); + int index = 0; + + System.arraycopy( tmp_bytes, 0, bytes, index, tmp_bytes.length ); + index += tmp_bytes.length; + bytes[index++] = (byte) PutMember; + bytes[index++] = sep; + + tmp_bytes = contextID.getBytes(); + System.arraycopy( tmp_bytes, 0, bytes, index, tmp_bytes.length ); + index += tmp_bytes.length; + bytes[index++] = sep; + + System.arraycopy( ticket_bytes, 0, bytes, index, ticket_bytes.length ); + index += ticket_bytes.length; + bytes[index++] = sep; + + System.arraycopy( ret_bytes, 0, bytes, index, ret_bytes.length ); + index += ret_bytes.length; + bytes[index++] = sep; + + signals.write( bytes, 0, bytes.length ); + } + public void sendSecurityConfirm( String [] certs, int certsnr, String perm, String id ) + { + Main.debug("sendSecurityConfirm, ID = " + id + " certsnr = " + certsnr); + + byte [] id_bytes = id.getBytes(); + byte [] perm_bytes = perm.getBytes(); + byte [] certsnr_bytes = String.valueOf( certsnr ).getBytes(); + int length = perm_bytes.length + id_bytes.length + certsnr_bytes.length + 5; + for (int i = 0; i < certsnr; i++) + length += certs[i].length() + 1; + byte [] bytes = new byte[ length + 8 ]; //for length of message + byte [] tmp_bytes = getPaddedLengthBytes( length ); + int index = 0; + + System.arraycopy( tmp_bytes, 0, bytes, index, tmp_bytes.length ); + index += tmp_bytes.length; + bytes[index++] = (byte) SecurityConfirmCode; + bytes[index++] = sep; + + System.arraycopy( id_bytes, 0, bytes, index, id_bytes.length ); + index += id_bytes.length; + bytes[index++] = sep; + + System.arraycopy( perm_bytes, 0, bytes, index, perm_bytes.length ); + index += perm_bytes.length; + bytes[index++] = sep; + + System.arraycopy( certsnr_bytes, 0, bytes, index, certsnr_bytes.length ); + index += certsnr_bytes.length; + bytes[index++] = sep; + + for (int i = 0; i < certsnr; i++) { + byte [] cert_bytes = certs[i].getBytes(); + System.arraycopy( cert_bytes, 0, bytes, index, cert_bytes.length ); + index += cert_bytes.length; + bytes[index++] = sep; + } + + signals.write( bytes, 0, bytes.length ); + } + /************************************************************** + ***** Utility functions for parsing commands **************** + **************************************************************/ + private String getArg( byte[] command ) + { + int begin = cmd_index; + while( 0 != ((int) command[cmd_index++]) ); + + if( cmd_index > (begin + 1) ) + { + String rval = new String( command, begin, (cmd_index - begin - 1) ); + return rval; + } + else + return null; + } + + private byte[] getPaddedLengthBytes( int length ) + { + byte[] length_bytes = String.valueOf( length ).getBytes(); + if( length_bytes.length > 8 ) + throw new IllegalArgumentException( "can't create string number of length = 8" ); + byte [] bytes = { (byte) ' ', (byte) ' ', (byte) ' ', (byte) ' ', + (byte) ' ', (byte) ' ', (byte) ' ', (byte) ' '}; + System.arraycopy( length_bytes, 0, bytes, 0, length_bytes.length ); + return bytes; + } + private int readPaddedLength( int string_size ) + throws IOException + { + //read in 8 bytes for command length- length will be sent as a padded string + byte[] length = new byte[string_size]; + commands.read( length, 0, string_size ); + + String length_str = new String( length ); + return Integer.parseInt( length_str.trim() ); + } + +} diff --git a/khtml/java/org/kde/kjas/server/KJASSecurityManager.java b/khtml/java/org/kde/kjas/server/KJASSecurityManager.java new file mode 100644 index 000000000..0525bba0c --- /dev/null +++ b/khtml/java/org/kde/kjas/server/KJASSecurityManager.java @@ -0,0 +1,243 @@ +package org.kde.kjas.server; + +import java.security.*; +import java.security.cert.*; +import java.net.*; +import java.util.*; + + +public class KJASSecurityManager extends SecurityManager +{ + static Hashtable confirmRequests = new Hashtable(); + static int confirmId = 0; + Hashtable grantedPermissions = new Hashtable(); + HashSet grantAllPermissions = new HashSet(); + HashSet rejectAllPermissions = new HashSet(); + + private static final char [] base64table = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' + }; + static String encode64( byte [] data) + { + StringBuffer buf = new StringBuffer( 4*((data.length + 2)/3) ); + int i = 0, b1, b2, b3; + while (i < data.length - 2) { + b1 = data[i++]; + b2 = data[i++]; + b3 = data[i++]; + buf.append( base64table[(b1 >>> 2) & 0x3F] ); + buf.append( base64table[((b1 << 4) & 0x30) | ((b2 >>> 4) & 0xF)] ); + buf.append( base64table[((b2 << 2) & 0x3C) | ((b3 >>> 6) & 0x03)] ); + buf.append( base64table[b3 & 0x3F] ); + } + if ( i < data.length ) { + b1 = data[i++]; + buf.append( base64table[(b1 >>> 2) & 0x3F] ); + if ( i < data.length ) { + b2 = data[i++]; + buf.append( base64table[((b1 << 4) & 0x30) | ((b2 >>> 4) & 0xF)] ); + buf.append( base64table[(b2 << 2) & 0x3C] ); + } else { + buf.append( base64table[(b1 << 4) & 0x30] ); + buf.append( "=" ); + } + buf.append( '=' ); + } + return buf.toString(); + } + public KJASSecurityManager() + { + } + /** + * checks for an applets permission to access certain resources + * currently, only a check for SocketPermission is done, that the + * applet cannot connect to any other but the host, where it comes from. + * Anything else seems to be handled automagically + */ + public void checkPermission(Permission perm) throws SecurityException, NullPointerException { + // ClassLoader cl = Thread.currentThread().getContextClassLoader(); + try { + super.checkPermission(perm); + } catch (SecurityException se) { + // Don't annoy users with these + if (/*perm instanceof java.lang.RuntimePermission || */ + perm instanceof java.awt.AWTPermission) + throw se; + + // Collect certificates + HashSet signers = new HashSet(); + Class [] cls = getClassContext(); + for (int i = 1; i < cls.length; i++) { + Object[] objs = cls[i].getSigners(); + if (objs != null && objs.length > 0) { + for (int j = 0; j < objs.length; j++) + if (objs[j] instanceof X509Certificate) + signers.add( ((X509Certificate) objs[j]) ); + } + } + Main.debug("Certificates " + signers.size() + " for " + perm); + + // Check granted/denied permission + if ( grantAllPermissions.contains(signers) ) + return; + if ( rejectAllPermissions.contains(signers) ) + throw se; + Permissions permissions = (Permissions) grantedPermissions.get(signers); + if (permissions != null && permissions.implies(perm)) + return; + + // Ok, ask user what to do + String [] certs = new String[signers.size()]; + int certsnr = 0; + for (Iterator i = signers.iterator(); i.hasNext(); ) { + try { + certs[certsnr] = encode64( ((X509Certificate) i.next()).getEncoded() ); + certsnr++; + } catch (CertificateEncodingException cee) {} + } + if (certsnr == 0) + throw se; + String id = "" + confirmId++; + confirmRequests.put(id, Thread.currentThread()); + Main.protocol.sendSecurityConfirm(certs, certsnr, perm.toString(), id); + boolean granted = false; + try { + Thread.sleep(300000); + } catch (InterruptedException ie) { + if (((String) confirmRequests.get(id)).equals("yes")) { + granted = true; + permissions = (Permissions) grantedPermissions.get(signers); + if (permissions == null) { + permissions = new Permissions(); + grantedPermissions.put(signers, permissions); + } + permissions.add(perm); + } else if (((String) confirmRequests.get(id)).equals("grant")) { + grantAllPermissions.add( signers ); + granted = true; + } else if (((String) confirmRequests.get(id)).equals("reject")) { + rejectAllPermissions.add( signers ); + } // else "no", "nossl" or "invalid" + } finally { + confirmRequests.remove(id); + } + if (!granted) { + Main.debug("Permission denied" + perm); + throw se; + } + } + } + + // keytool -genkey -keystore mystore -alias myalias + // keytool -export -keystore mystore -alias myalias -file mycert + // keytool -printcert -file mycert + // keytool -import -keystore myotherstore -alias myalias -file mycert + // jarsigner -keystore mystore myjar.jar myalias + // jarsigner -verify -keystore myotherstore myjar.jar + // + // policy file (use policytool and check java.security): + // keystore "file:myotherstore", "JKS" + // grant signedBy "myalias" + // { + // permission java.io.FilePermission "<<ALL FILES>>", "read" + // } + // + // java code: + // KeyStore store = KeyStore.getInstance("JKS", "SUN"); + public void disabled___checkPermission(Permission perm) throws SecurityException, NullPointerException + { + // does not seem to work as expected, Problems with proxy - and it seems that the default + // implementation already does all that well, what I wanted to do here. + // It is likely that this method will hence disappear soon again. + Object context = getSecurityContext(); + Thread thread = Thread.currentThread(); + if (perm instanceof SocketPermission) { + // check if this is a connection back to the originating host + // if not, fall through and call super.checkPermission + // this gives normally access denied + Main.debug("*** checkPermission " + perm + " in context=" + context + " Thread=" + thread); + // use the context class loader to determine if this is one + // of our applets + ClassLoader contextClassLoader = thread.getContextClassLoader(); + Main.debug("* ClassLoader=" + contextClassLoader); + try { + // try to cast ... + KJASAppletClassLoader loader = (KJASAppletClassLoader)contextClassLoader; + // ok. cast succeeded. Now get the codebase of the loader + // because it contains the host name + URL codebase = loader.getCodeBase(); + URL docbase = loader.getDocBase(); + Main.debug("* Class Loader docbase=" + docbase + " codebase=" + codebase); + String hostname = perm.getName(); + // extract the hostname from the permission name + // which is something like "some.host.domain:XX" + // with XX as the port number + int colonIdx = hostname.indexOf(':'); + if (colonIdx > 0) { + // strip of the port + hostname = hostname.substring(0, colonIdx); + } + // Main.info("Checking " + hostname + "<->" + codebase.getHost()); + + if (hostsAreEqual(hostname, codebase.getHost())) { + // ok, host matches + String actions = perm.getActions(); + // just check if listen is specified which we do not want + // to allow + if (actions != null && actions.indexOf("listen") >= 0) { + Main.debug("* Listen is not allowed."); + } else { + // ok, just return and throw _no_ exception + Main.debug("* Hostname equals. Permission granted."); + return; + } + } else { + Main.info("Host mismatch: " + perm + " != " + codebase.getHost()); + } + } catch (ClassCastException e) { + Main.debug("* ClassLoader is not a KJASAppletClassLoader"); + } + Main.debug("* Fall through to super.checkPermission()"); + } + super.checkPermission(perm); + } + + private static final boolean hostsAreEqual(String host1, String host2) { + if (host1 == null || host2 == null) { + return false; + } + if (host1.length() == 0 || host2.length() == 0) { + return false; + } + if (host1.equalsIgnoreCase(host2)) { + return true; + } + + if ( Main.proxyHost != null && Main.proxyPort != 0) { + // if we use a proxy, we certainly cannot use DNS + return false; + } + + InetAddress inet1=null, inet2=null; + try { + inet1 = InetAddress.getByName(host1); + } catch (UnknownHostException e) { + Main.kjas_err("Unknown host:" + host1, e); + return false; + } + try { + inet2 = InetAddress.getByName(host2); + } catch (UnknownHostException e) { + Main.kjas_err("Unknown host:" + host2, e); + return false; + } + if (inet1.equals(inet2)) { + return true; + } + return false; + } +} diff --git a/khtml/java/org/kde/kjas/server/KJASSoundPlayer.java b/khtml/java/org/kde/kjas/server/KJASSoundPlayer.java new file mode 100644 index 000000000..a37c2bbd4 --- /dev/null +++ b/khtml/java/org/kde/kjas/server/KJASSoundPlayer.java @@ -0,0 +1,36 @@ +package org.kde.kjas.server; + +import java.applet.*; +import java.net.*; + +public class KJASSoundPlayer implements AudioClip +{ + private String file; + private String contextId; + + public KJASSoundPlayer( String _contextId, URL _file ) + { + file = _file.toString(); + contextId = _contextId; + Main.debug("KJASSoundPlayer( URL '" + _file + "')"); + } + + public void loop() + { + Main.debug("KJASSoundPlayer loop() URL='" + file + "'"); + Main.protocol.sendAudioClipLoopCommand(contextId, file); + } + + public void play() + { + Main.debug("KJASSoundPlayer play() URL='" + file + "'"); + Main.protocol.sendAudioClipPlayCommand(contextId, file); + } + + public void stop() + { + Main.debug("KJASSoundPlayer stop() URL='" + file + "'"); + Main.protocol.sendAudioClipStopCommand(contextId, file); + } +} + diff --git a/khtml/java/org/kde/kjas/server/KJASSwingConsole.java b/khtml/java/org/kde/kjas/server/KJASSwingConsole.java new file mode 100644 index 000000000..fedda6c48 --- /dev/null +++ b/khtml/java/org/kde/kjas/server/KJASSwingConsole.java @@ -0,0 +1,325 @@ +/* This file is part of the KDE project + * + * Copyright (C) 2002 Till + * Copyright (C) 2005 Koos Vriezen <koos ! vriezen () xs4all ! nl> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +package org.kde.kjas.server; + +import java.awt.Toolkit; +import java.awt.Image; +import java.awt.BorderLayout; +import java.awt.event.ActionListener; +import java.awt.event.ActionEvent; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.PrintStream; +import java.util.Enumeration; +import java.util.Properties; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JButton; +import javax.swing.JTextArea; +import javax.swing.border.EmptyBorder; + + +public class KJASSwingConsole implements Console { + private JFrame frame = null; + private JPanel jPanel1; + private JScrollPane jScrollPane1; + private JButton clearButton; + private JTextArea textField; + private JButton closeButton; + private JButton copyButton; + final static int NR_BUFFERS = 3; + final static int MAX_BUF_LENGTH = 3000; + private int queue_pos = 0; + private StringBuffer [] output_buffer = new StringBuffer[NR_BUFFERS]; + + private PrintStream real_stderr = new PrintStream(System.err); + + /** Creates new form KJASSwingConsole */ + public KJASSwingConsole() { + PrintStream st = new PrintStream( new KJASConsoleStream(this) ); + System.setOut(st); + System.setErr(st); + } + + private void initComponents() { + frame = new JFrame("Konqueror Java Console"); + jPanel1 = new JPanel(); + clearButton = new JButton(); + closeButton = new JButton(); + copyButton = new JButton(); + jScrollPane1 = new JScrollPane(); + textField = new JTextArea(); + + frame.setFont(new java.awt.Font("Monospaced", 0, 10)); + frame.setName("KJAS Console"); + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent evt) { + exitForm(evt); + } + }); + + jPanel1.setLayout(new BorderLayout()); + jPanel1.setBorder(new EmptyBorder(new java.awt.Insets(1, 1, 1, 1))); + clearButton.setText("clear"); + clearButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + clearButtonActionPerformed(evt); + } + }); + + jPanel1.add(clearButton, BorderLayout.WEST); + + closeButton.setText("close"); + closeButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + closeButtonActionPerformed(evt); + } + }); + + jPanel1.add(closeButton, BorderLayout.EAST); + + copyButton.setText("copy"); + copyButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + copyButtonActionPerformed(evt); + } + }); + + jPanel1.add(copyButton, BorderLayout.CENTER); + + frame.getContentPane().add(jPanel1, BorderLayout.SOUTH); + + textField.setColumns(40); + textField.setEditable(false); + textField.setRows(10); + textField.addKeyListener(new KeyAdapter() { + public void keyPressed(KeyEvent evt) { + textFieldKeyPressed(evt); + } + }); + + jScrollPane1.setViewportView(textField); + + frame.getContentPane().add(jScrollPane1, BorderLayout.CENTER); + + try { + java.net.URL iconUrl = getClass().getClassLoader().getResource("images/beanicon.png"); + if (iconUrl != null) { + Toolkit tk = Toolkit.getDefaultToolkit(); + Image icon = tk.createImage(iconUrl); + frame.setIconImage(icon); + } + } catch (Throwable e) { + } + frame.pack(); + frame.setSize(500, 300); + } + + private void textFieldKeyPressed(java.awt.event.KeyEvent evt) { + // Add your handling code here: + char key = evt.getKeyChar(); + switch (key) { + case 'h': + showHelp(); + break; + case 'g': + append("Running Garbage Collection ...\n", true); + System.gc(); + case 'm': + append("Total Memory: " + Runtime.getRuntime().totalMemory() + " bytes\n", true); + append("Free Memory : " + Runtime.getRuntime().freeMemory() + " bytes\n", true); + break; + case 'c': + clear(); + break; + case 's': + showSystemProperties(); + break; + case 't': + showThreads(); + break; + case 'x': + KJASAppletClassLoader.removeLoaders(); + append("Emptied Classloader Cache\n", true); + break; + } + } + + private void showHelp() { + append("Java VM: " + System.getProperty("java.vendor") + " " + System.getProperty("java.version") + "\n", true); + String ph = System.getProperty("http.proxyHost"); + if (ph != null) { + append("Proxy: " + ph + ":" + System.getProperty("java.proxyPort") + "\n", true); + } + SecurityManager sec = System.getSecurityManager(); + if (sec == null) { + append("WARNING: Security Manager disabled!\n", true); + } else { + append("SecurityManager=" + sec + "\n", true); + } + appendSeparator(); + append("Konqueror Java Console Help\n", true); + append(" c: clear console\n", true); + append(" g: run garbage collection\n", true); + append(" h: show help\n", true); + append(" m: show memory info\n", true); + append(" s: print system properties\n", true); + append(" t: list threads\n", true); + append(" x: empty classloader cache\n", true); + appendSeparator(); + } + + private void showSystemProperties() { + append("Printing System Properties ...\n", true); + appendSeparator(); + Properties p = System.getProperties(); + for (Enumeration e = p.keys(); e.hasMoreElements();) { + Object key = e.nextElement(); + if ("line.separator".equals(key)) { + String value = (String) p.get(key); + StringBuffer unescaped = new StringBuffer(10); + for (int i = 0; i < value.length(); i++) { + char c = value.charAt(i); + if (c == '\n') unescaped.append("\\n"); + else if (c == '\r') unescaped.append("\\n"); + else unescaped.append(c); + } + append(key + " = " + unescaped + "\n", true); + } else append(key + " = " + p.get(key) + "\n", true); + } + appendSeparator(); + } + + private void showThreads() { + Thread t = Thread.currentThread(); + ThreadGroup g = t.getThreadGroup(); + ThreadGroup parent; + while ((parent = g.getParent()) != null) { + g = parent; + } + g.list(); + } + + private void copyButtonActionPerformed(java.awt.event.ActionEvent evt) { + textField.selectAll(); + textField.copy(); + } + + private void closeButtonActionPerformed(java.awt.event.ActionEvent evt) { + frame.setVisible(false); + } + + private void clearButtonActionPerformed(java.awt.event.ActionEvent evt) { + clear(); + } + + /** Exit the Application */ + private void exitForm(java.awt.event.WindowEvent evt) { + frame.setVisible(false); + } + + public void setVisible(boolean visible) { + if (frame == null && visible) { + initComponents(); + frame.setVisible(visible); + System.out.println( "Java VM version: " + + System.getProperty("java.version") ); + System.out.println( "Java VM vendor: " + + System.getProperty("java.vendor") ); + String ph = System.getProperty("http.proxyHost"); + String pp = System.getProperty("http.proxyPort"); + if (ph != null) { + System.out.println("Proxy: " + ph + ":" + pp); + } + SecurityManager sec = System.getSecurityManager(); + Main.debug("SecurityManager=" + sec); + if (sec == null) { + System.out.println( "WARNING: Security Manager disabled!" ); + textField.setForeground(java.awt.Color.red); + } + showHelp(); + } else if (frame != null) + frame.setVisible(visible); + + if (visible) { + for (int i = 0; i < NR_BUFFERS; i++) + if (output_buffer[(queue_pos + i + 1) % 3] != null) { + textField.append(output_buffer[(queue_pos + i + 1) % 3].toString()); + output_buffer[(queue_pos + i + 1) % 3] = null; + } + } + } + + /** + * @param args the command line arguments + */ + public static void main(String args[]) { + new KJASSwingConsole().setVisible(true); + } + + public void clear() { + textField.setText(""); + } + + private void appendSeparator() { + append("----------------------------------------------------\n", true); + } + + public void append(String txt) { + append(txt, false); + } + + public void append(String txt, boolean force) { + if (txt == null) + return; + if (frame == null || !frame.isVisible()) { + if (Main.Debug) + real_stderr.print(txt); + if (output_buffer[queue_pos] != null && + output_buffer[queue_pos].length() > MAX_BUF_LENGTH) { + queue_pos = (++queue_pos) % NR_BUFFERS; + if (output_buffer[queue_pos] != null) { + // starting overwriting old log, clear textField if exists + if (frame != null) + textField.setText(""); + output_buffer[queue_pos] = null; + } + } + if (output_buffer[queue_pos] == null) + output_buffer[queue_pos] = new StringBuffer(txt); + else + output_buffer[queue_pos].append(txt); + return; + } + int length = txt.length(); + synchronized(textField) { + //get the caret position, and then get the new position + int old_pos = textField.getCaretPosition(); + textField.append(txt); + textField.setCaretPosition( old_pos + length ); + } + } +} + diff --git a/khtml/java/org/kde/kjas/server/KJASURLStreamHandlerFactory.java b/khtml/java/org/kde/kjas/server/KJASURLStreamHandlerFactory.java new file mode 100644 index 000000000..58c5ff518 --- /dev/null +++ b/khtml/java/org/kde/kjas/server/KJASURLStreamHandlerFactory.java @@ -0,0 +1,609 @@ +/* This file is part of the KDE project + * + * Copyright (C) 2003 Koos Vriezen <koos ! vriezen () xs4all ! nl> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +package org.kde.kjas.server; + +import java.net.*; +import java.io.*; +import java.util.*; +import java.security.*; +/** + * + */ + +class KIOConnection +{ + final static int NOT_CONNECTED = 0; + final static int CONNECT_WAIT = 1; + final static int CONNECTED = 2; + + final static int DATA = 0; + final static int FINISHED = 1; + final static int ERRORCODE = 2; + final static int CONNECT = 6; + final static int REQUESTDATA = 7; + + final static int STOP = 0; + final static int HOLD = 1; + final static int RESUME = 2; + + protected static int id = 0; + static Hashtable jobs = new Hashtable(); // should be thread safe + + static void setData(String jobid, int code, byte [] data) { + KIOConnection job = (KIOConnection) jobs.get(jobid); + if (job == null || !job.setData(code, data)) + Main.info("KIO KJASHttpURLConnection gone (timedout/closed)"); + else + Thread.yield(); + } + + private class KJASOutputStream extends OutputStream { + KJASOutputStream() { + } + public void write(int b) throws IOException { + byte[] buf = {(byte)b}; + write(buf); + } + public synchronized void write(byte b[], int off, int len) throws IOException { + byte[] buf = new byte[len]; + System.arraycopy(b, off, buf, 0, len); + sendData(buf, false); + } + public void write(byte b[]) throws IOException { + write(b, 0, b.length); + } + public void close() throws IOException { + disconnect(); + } + public void flush() throws IOException { + checkConnected(); + sendData(null, true); + } + } + + private class KJASInputStream extends InputStream { + + KJASInputStream() { + } + public int read() throws IOException { + if (getData(true)) + return 0x00ff & in_buf[in_bufpos++]; + return -1; + } + public int read(byte[] b, int off, int len) throws IOException { + int total = 0; + do { + if (!getData(true)) break; + int nr = in_buf.length - in_bufpos; + if (nr > len) + nr = len; + System.arraycopy(in_buf, in_bufpos, b, off, nr); + len -= nr; + total += nr; + off += nr; + in_bufpos += nr; + } while (len > 0); + return total > 0 ? total : -1; + } + public int read(byte[] b) throws IOException { + return read(b, 0, b.length); + } + public int available() throws IOException { + return inAvailable(); + } + public boolean markSupported() { + return false; + } + public void close() throws IOException { + disconnect(); + } + } + + protected URL url; + protected int connect_status = 0; + protected String jobid = null; // connection id with KIO + protected LinkedList data = new LinkedList(); // not thread safe + protected int errorcode = 0; + protected boolean finished = false; // all data has arived + protected boolean onhold = false; // KIO job is suspended + protected boolean request_data = false; // need data for put job + private KJASOutputStream out = null; + private KJASInputStream in = null; + private byte [] in_buf = null; // current input buffer + private int in_bufpos = 0; // position in buffer + private boolean in_eof = false; // all data is read + private final static int LOW_BUFFER_LIMIT = 5; // put onhold off + private final static int HIGH_BUFFER_LIMIT = 10; // put onhold on + + protected KIOConnection(URL u) { + url = u; + } + protected void checkConnected() throws IOException { + if (connect_status != CONNECTED) + throw new IOException("not connected"); + } + protected boolean haveError() { + return errorcode != 0; + } + synchronized protected boolean setData(int code, byte [] d) { + // is job still there when entering the monitor + if (jobs.get(jobid) == null) + return false; + if (connect_status == CONNECT_WAIT) + connect_status = CONNECTED; + switch (code) { + case FINISHED: + if (d != null && d.length > 0) + data.addLast(d); + finished = true; + onhold = false; + jobs.remove(jobid); + Main.debug ("KIO FINISHED (" + jobid + ") " + data.size()); + break; + case DATA: + if (d.length > 0) + data.addLast(d); + // Main.debug ("KIO DATA (" + jobid + ") " + data.size()); + if (!onhold && data.size() > HIGH_BUFFER_LIMIT) { + Main.protocol.sendDataCmd(jobid, HOLD); + onhold = true; + } + break; + case ERRORCODE: + String codestr = new String(d); + errorcode = Integer.parseInt(codestr); + Main.debug ("KIO ERRORECODE(" + jobid + ") " + errorcode); + break; + case CONNECT: + Main.debug ("KIO CONNECT(" + jobid + ") "); + request_data = true; + errorcode = 0; + break; + case REQUESTDATA: + Main.debug ("KIO REQUESTDATA(" + jobid + ") "); + request_data = true; + break; + } + notifyAll(); + return true; + } + + private synchronized boolean getData(boolean mayblock) throws IOException { + if (haveError()) { + //disconnect(); + in_eof = true; + //throw new IOException("i/o error " + errorcode); + } + if (in_eof) + return false; + checkConnected(); + if (in_buf != null && in_bufpos < in_buf.length) + return true; + int datasize = data.size(); + if (datasize > 0) { + in_buf = (byte []) data.removeFirst(); + in_bufpos = 0; + } + if (onhold && datasize < LOW_BUFFER_LIMIT) { + Main.protocol.sendDataCmd(jobid, RESUME); + onhold = false; + } + if (datasize > 0) + return true; + if (finished) { + in_eof = true; + return false; + } + if (!mayblock) + return false; + try { + wait(); + } catch (InterruptedException ie) { + return false; + } + return getData(false); + } + synchronized private int inAvailable() throws IOException { + if (in_eof) + return 0; + checkConnected(); + if (!getData(false)) + return 0; + int total = in_buf.length - in_bufpos; + ListIterator it = data.listIterator(0); + while (it.hasNext()) + total += ((byte []) it.next()).length; + return total; + } + synchronized private void sendData(byte [] d, boolean force) throws IOException { + Main.debug ("KIO sendData(" + jobid + ") force:" + force + " request_data:" + request_data); + if (d != null) + data.addLast(d); + if (!request_data && !force) return; + if (data.size() == 0) return; + if (force && !request_data) { + try { + wait(10000); + } catch (InterruptedException ie) { + return; + } + if (!request_data) { + Main.debug ("KIO sendData(" + jobid + ") timeout"); + data.clear(); + disconnect(); + throw new IOException("timeout"); + } + } + byte[] buf; + int total = 0; + ListIterator it = data.listIterator(0); + while (it.hasNext()) + total += ((byte []) it.next()).length; + buf = new byte[total]; + int off = 0; + it = data.listIterator(0); + while (it.hasNext()) { + byte [] b = (byte []) it.next(); + System.arraycopy(b, 0, buf, off, b.length); + off += b.length; + } + data.clear(); + request_data = false; + Main.protocol.sendPutData(jobid, buf, 0, total); + } + synchronized void connect(boolean doInput) throws IOException { + if (connect_status == CONNECTED) + return; // javadocs: call is ignored + //(new Exception()).printStackTrace(); + Main.debug ("KIO connect " + url); + errorcode = 0; + finished = in_eof = false; + jobid = String.valueOf(id++); + jobs.put(jobid, this); + if (doInput) + Main.protocol.sendGetURLDataCmd(jobid, url.toExternalForm()); + else + Main.protocol.sendPutURLDataCmd(jobid, url.toExternalForm()); + connect_status = CONNECT_WAIT; + try { + wait(20000); + } catch (InterruptedException ie) { + errorcode = -1; + } + boolean isconnected = (connect_status == CONNECTED); + if (isconnected && !haveError()) { + if (doInput) + in = new KJASInputStream(); + else + out = new KJASOutputStream(); + Main.debug ("KIO connect(" + jobid + ") " + url); + return; + } + connect_status = NOT_CONNECTED; + jobs.remove(jobid); + if (isconnected) { + if (!finished) + Main.protocol.sendDataCmd(jobid, STOP); + Main.debug ("KIO connect error " + url); + throw new ConnectException("connection failed (not found)"); + } + Main.debug ("KIO connect timeout " + url); + throw new IOException("connection failed (timeout)"); + } + synchronized void disconnect() { + if (connect_status == NOT_CONNECTED) + return; + Main.debug ("KIO disconnect " + jobid); + //(new Exception()).printStackTrace(); + if (out != null) { + try { + out.flush(); + } catch (IOException iox) {} + } + connect_status = NOT_CONNECTED; + out = null; + in = null; + if (!finished) { + Main.protocol.sendDataCmd(jobid, STOP); + jobs.remove(jobid); + } + notifyAll(); + } + InputStream getInputStream() throws IOException { + Main.debug ("KIO getInputStream(" + jobid + ") " + url); + return in; + } + OutputStream getOutputStream() throws IOException { + Main.debug ("KIO getOutputStream(" + jobid + ") " + url); + return out; + } +} + +final class KIOHttpConnection extends KIOConnection +{ + final static int HEADERS = 3; + final static int REDIRECT = 4; + final static int MIMETYPE = 5; + + Vector headers = new Vector(); + Hashtable headersmap = new Hashtable(); + String responseMessage = null; + int responseCode = -1; + + KIOHttpConnection(URL u) { + super(u); + } + protected boolean haveError() { + return super.haveError() || + responseCode != 404 && (responseCode < 0 || responseCode >= 400); + } + protected synchronized boolean setData(int code, byte [] d) { + switch (code) { + case HEADERS: + StringTokenizer tokenizer = new StringTokenizer(new String(d), "\n"); + while (tokenizer.hasMoreTokens()) { + String token = tokenizer.nextToken(); + int pos = token.indexOf(":"); + String [] entry = { + token.substring(0, pos > -1 ? pos : token.length()).toLowerCase(), token.substring(pos > -1 ? pos+1: token.length()).trim() + }; + headers.add(entry); + headersmap.put(entry[0], entry[1]); + // Main.debug ("KIO header " + entry[0] + "=" + entry[1]); + } + responseCode = 0; + if (headersmap.size() > 0) { + String token = ((String []) headers.get(0))[0]; + if (!token.startsWith("http/1.")) break; + int spos = token.indexOf(' '); + if (spos < 0) break; + int epos = token.indexOf(' ', spos + 1); + if (epos < 0) break; + responseCode = Integer.parseInt(token.substring(spos+1, epos)); + responseMessage = token.substring(epos+1); + Main.debug ("KIO responsecode=" + responseCode); + } + break; + } + return super.setData(code, d); + } +} + +final class KIOSimpleConnection extends KIOConnection +{ + KIOSimpleConnection(URL u) { + super(u); + } +} + +final class KJASHttpURLConnection extends HttpURLConnection +{ + private KIOHttpConnection kioconnection; + + KJASHttpURLConnection(URL u) { + super(u); + kioconnection = new KIOHttpConnection(u); + } + public Map getHeaderFields() { + try { + connect(); + } catch (IOException e) { + Main.debug ("Error on implicit connect()"); + } + Main.debug ("KIO getHeaderFields"); + return kioconnection.headersmap; + } + public String getHeaderField(String name) { + try { + connect(); + } catch (IOException e) { + Main.debug ("Error on implicit connect()"); + } + String field = (String) kioconnection.headersmap.get(name); + Main.debug ("KIO getHeaderField:" + name + "=" + field); + //(new Exception()).printStackTrace(); + return field; + } + public String getHeaderField(int n) { + try { + connect(); + } catch (IOException e) { + Main.debug ("Error on implicit connect()"); + } + Main.debug ("KIO getHeaderField(" + n + ") size=" + kioconnection.headersmap.size()); + if (n >= kioconnection.headersmap.size()) + return null; + String [] entry = (String []) kioconnection.headers.get(n); + String line = entry[0]; + if (entry[1].length() > 0) + line += ":" + entry[1]; + Main.debug ("KIO getHeaderField(" + n + ")=#" + line + "#"); + return line; + } + public String getHeaderFieldKey(int n) { + try { + connect(); + } catch (IOException e) { + Main.debug ("Error on implicit connect()"); + } + Main.debug ("KIO getHeaderFieldKey " + n); + if (n >= kioconnection.headersmap.size()) + return null; + return ((String []) kioconnection.headers.get(n))[0]; + } + public int getResponseCode() throws IOException { + Main.debug ("KIO getResponseCode"); + if (kioconnection.responseCode == -1) { + try { + connect(); + } catch (IOException e) { + if (kioconnection.responseCode == -1) + throw e; + } + } + responseMessage = kioconnection.responseMessage; + return kioconnection.responseCode; + } + public boolean usingProxy() { + return false; // FIXME + } + public void connect() throws IOException { + if (connected) + return; + Main.debug ("KIO KJASHttpURLConnection.connect " + url); + SecurityManager security = System.getSecurityManager(); + if (security != null) + security.checkPermission(getPermission()); + kioconnection.connect(doInput); + connected = true; + if (kioconnection.responseCode == 404) + throw new FileNotFoundException(url.toExternalForm()); + } + public void disconnect() { + kioconnection.disconnect(); + connected = false; + } + public InputStream getInputStream() throws IOException { + doInput = true; + doOutput = false; + connect(); + return kioconnection.getInputStream(); + } + public OutputStream getOutputStream() throws IOException { + doInput = false; + doOutput = true; + connect(); + return kioconnection.getOutputStream(); + } + public InputStream getErrorStream() { + Main.debug("KIO KJASHttpURLConnection.getErrorStream" + url); + try { + if (connected && kioconnection.responseCode == 404) + return kioconnection.getInputStream(); + } catch (Exception ex) {} + return null; + } +} + +final class KJASSimpleURLConnection extends URLConnection +{ + private KIOSimpleConnection kioconnection = null; + private int default_port; + + KJASSimpleURLConnection(URL u, int p) { + super(u); + default_port = p; + } + public boolean usingProxy() { + return false; // FIXME + } + public Permission getPermission() throws IOException { + int p = url.getPort(); + if (p < 0) + p = default_port; + return new SocketPermission(url.getHost() + ":" + p, "connect"); + } + public void connect() throws IOException { + if (kioconnection != null) + return; + Main.debug ("KIO KJASSimpleURLConnection.connection " + url); + SecurityManager security = System.getSecurityManager(); + if (security != null) + security.checkPermission(getPermission()); + kioconnection = new KIOSimpleConnection(url); + kioconnection.connect(doInput); + connected = true; + } + public void disconnect() { + if (kioconnection == null) + return; + kioconnection.disconnect(); + kioconnection = null; + connected = false; + } + public InputStream getInputStream() throws IOException { + doInput = true; + doOutput = false; + if (kioconnection == null) + connect(); + return kioconnection.getInputStream(); + } + public OutputStream getOutputStream() throws IOException { + doInput = false; + doOutput = true; + if (kioconnection == null) + connect(); + return kioconnection.getOutputStream(); + } +} + + +final class KJASHttpURLStreamHandler extends URLStreamHandler +{ + KJASHttpURLStreamHandler(int port) { + default_port = port; + } + protected URLConnection openConnection(URL u) throws IOException { + URL url = new URL(u.toExternalForm()); + return new KJASHttpURLConnection(url); + } + protected int getDefaultPort() { + return default_port; + } + private int default_port; +} + +final class KJASSimpleURLStreamHandler extends URLStreamHandler +{ + KJASSimpleURLStreamHandler(int port) { + default_port = port; + } + protected URLConnection openConnection(URL u) throws IOException { + URL url = new URL(u.toExternalForm()); + return new KJASSimpleURLConnection(url, default_port); + } + protected int getDefaultPort() { + return default_port; + } + private int default_port; +} + +public final class KJASURLStreamHandlerFactory + implements URLStreamHandlerFactory +{ + public URLStreamHandler createURLStreamHandler(String protocol) { + if (protocol.equals("jar") || protocol.equals("file")) + return null; + //outputs to early: Main.debug ("createURLStreamHandler " + protocol); + Main.debug ("KIO createURLStreamHandler " + protocol); + if (protocol.equals("http")) + return new KJASHttpURLStreamHandler(80); + else if (protocol.equals("https")) + return new KJASHttpURLStreamHandler(443); + else if (protocol.equals("ftp")) + return new KJASSimpleURLStreamHandler(21); + else if (protocol.equals("smb")) + return new KJASSimpleURLStreamHandler(139); + else if (protocol.equals("fish")) + return new KJASSimpleURLStreamHandler(22); + return null; + } +} diff --git a/khtml/java/org/kde/kjas/server/Main.java b/khtml/java/org/kde/kjas/server/Main.java new file mode 100644 index 000000000..50ccb14f1 --- /dev/null +++ b/khtml/java/org/kde/kjas/server/Main.java @@ -0,0 +1,178 @@ +package org.kde.kjas.server; + +import java.io.*; +import java.security.*; +import java.net.*; + +/** + * KJAS server recognizes these variablers: + * kjas.debug - makes server actions verbose + * kjas.showConsole - shows Java Console window + * kjas.log - save a transcript of the debug output to /tmp/kjas.log + */ + +public class Main +{ + //We need to save a reference to the original stdout + //for sending messages back + static final KJASProtocolHandler protocol; + static Console console = null; + private static final boolean show_console; + public static final boolean Debug; + public static final boolean log; + static final boolean cacheImages; + static float java_version = (float) 0.0; + static String proxyHost = null; + static int proxyPort = 0; + private static boolean good_jdk = true; + + /************************************************************************** + * Initialization + **************************************************************************/ + static + { + Debug = System.getProperty( "kjas.debug" ) != null; + + show_console = System.getProperty( "kjas.showConsole" ) != null; + + if( System.getProperty( "kjas.useKio" ) != null ) + URL.setURLStreamHandlerFactory( new KJASURLStreamHandlerFactory() ); + + log = System.getProperty( "kjas.log" ) != null; + + cacheImages = System.getProperty( "kjas.noImageCache" ) != null; + + // determine system proxy + proxyHost = System.getProperty( "http.proxyHost" ); + String proxyPortString = System.getProperty( "http.proxyPort" ); + try { + proxyPort = Integer.parseInt(proxyPortString); + } catch (Exception e) { + } + //Main.debug( "JVM version = " + System.getProperty( "java.version" ) ); + String version = System.getProperty("java.version").substring( 0, 3 ); + // Hack for SGI Java2 runtime + if (version == "Jav") { // Skip over JavaVM- (the first 7 chars) + version = System.getProperty("java.version").substring(7,3); + } + //Main.debug( "JVM numerical version = " + version ); + try { + java_version = Float.parseFloat( version ); + if( java_version < 1.2 ) + good_jdk = false; + } catch( NumberFormatException e ) { + good_jdk = false; + } + PrintStream protocol_stdout = System.out; + console = new KJASSwingConsole(); + protocol = new KJASProtocolHandler( System.in, protocol_stdout ); + } + + /************************************************************************** + * Public Utility functions available to the KJAS framework + **************************************************************************/ + public static void debug( String msg ) + { + if( Debug ) + { + System.out.println( "KJAS: " + msg ); + } + } + public static void info (String msg ) { + System.err.println( "KJAS: " + msg ); + } + + public static void kjas_err( String msg, Exception e ) + { + System.err.println( msg ); + System.err.println( "Backtrace: " ); + e.printStackTrace(); + } + + public static void kjas_err( String msg, Throwable t ) + { + System.err.println( msg ); + t.printStackTrace(); + } + private Main() { + } + + /************************************************************************** + * Main- create the command loop + **************************************************************************/ + public static void main( String[] args ) + { + if( !good_jdk ) + { + console.setVisible( true ); + System.err.println( "ERROR: This version of Java is not supported for security reasons." ); + System.err.println( "\t\tPlease use Java version 1.2 or higher." ); + return; + } + + if( show_console ) + console.setVisible( true ); + + // set up https + boolean hasHTTPS = true; + + try { + // https needs a secure socket provider + Provider[] sslProviders = Security.getProviders("SSLContext.SSL"); + + if (sslProviders == null || sslProviders.length == 0) { + // as a fallback, try to dynamically install Sun's jsse + Class provider = Class.forName("com.sun.net.ssl.internal.ssl.Provider"); + + if (provider != null) { + Main.debug("adding Security Provider"); + Provider p = (Provider) provider.newInstance(); + Security.addProvider(p); + } else { + // Try jessie (http://www.nongnu.org/jessie/) as a fallback + // available in the Free World + provider = Class.forName("org.metastatic.jessie.provider.Jessie"); + if (provider != null) { + Main.debug("adding Jessie as Security Provider"); + Provider p = (Provider) provider.newInstance(); + Security.addProvider(p); + } else { + Main.debug("could not get class: com.sun.net.ssl.internal.ssl.Provider"); + hasHTTPS = false; + } + } + } + + if (hasHTTPS) { + // allow user to provide own protocol handler + // -Djava.protocol.handler.pkgs = user.package.name + // getting and setting of properties might generate SecurityExceptions + // so this needs to be in a try block + String handlerPkgs = System.getProperty("java.protocol.handler.pkgs"); + + if (handlerPkgs == null) { + // set default packages for Sun and IBM + handlerPkgs = "com.sun.net.ssl.internal.www.protocol" + + "|com.ibm.net.ssl.www.protocol"; + } else { + // add default packages for Sun and IBM as fallback + handlerPkgs += "|com.sun.net.ssl.internal.www.protocol" + + "|com.ibm.net.ssl.www.protocol"; + } + + System.setProperty("java.protocol.handler.pkgs", handlerPkgs); + } + } catch (Exception e) { + hasHTTPS = false; + } + + if (hasHTTPS == false) { + System.out.println("Unable to load JSSE SSL stream handler, https support not available"); + System.out.println("For more information see http://java.sun.com/products/jsse/"); + } + + //start the command parsing + protocol.commandLoop(); + } + +} diff --git a/khtml/java/org/kde/kjas/server/StatusListener.java b/khtml/java/org/kde/kjas/server/StatusListener.java new file mode 100644 index 000000000..be17c2b29 --- /dev/null +++ b/khtml/java/org/kde/kjas/server/StatusListener.java @@ -0,0 +1,5 @@ +package org.kde.kjas.server; + +public interface StatusListener { + public void showStatus(String message); +}
\ No newline at end of file diff --git a/khtml/java/pluginsinfo b/khtml/java/pluginsinfo new file mode 100644 index 000000000..7d37ed7c8 --- /dev/null +++ b/khtml/java/pluginsinfo @@ -0,0 +1,7 @@ +number=1 + +[0] +description=Java Plug-in KJAS for Konqueror +file=kjavaappletviewer.so +mime=application/x-java-applet:class:Java Applet;application/x-java-bean:jar:JavaBeans +name=Java Plug-in diff --git a/khtml/java/tests/Makefile.am b/khtml/java/tests/Makefile.am new file mode 100644 index 000000000..ab392b874 --- /dev/null +++ b/khtml/java/tests/Makefile.am @@ -0,0 +1,9 @@ +INCLUDES = -I$(top_srcdir)/khtml/java -I$(top_srcdir)/khtml \ + -I$(top_srcdir) $(all_includes) + +check_PROGRAMS = testserver + +testserver_SOURCES = testkjavaappletserver.cpp +testserver_LDADD = ../libkjava.la + +METASOURCES = AUTO diff --git a/khtml/java/tests/badapplets/BadApplet.jar b/khtml/java/tests/badapplets/BadApplet.jar Binary files differnew file mode 100644 index 000000000..fc9c274a2 --- /dev/null +++ b/khtml/java/tests/badapplets/BadApplet.jar diff --git a/khtml/java/tests/badapplets/BadApplet.java b/khtml/java/tests/badapplets/BadApplet.java new file mode 100644 index 000000000..c8217d256 --- /dev/null +++ b/khtml/java/tests/badapplets/BadApplet.java @@ -0,0 +1,202 @@ +import java.awt.*; +import java.awt.event.*; +import java.applet.*; +import javax.swing.*; +import java.io.*; +import java.net.*; +import java.awt.datatransfer.*; + +public class BadApplet extends JApplet { + JTabbedPane tabs = new JTabbedPane(); + JPanel FileSystemTests = new JPanel(); + JPanel NetworkTests = new JPanel(); + JPanel EnvironmentTests = new JPanel(); + + JButton writeFileButton = new JButton("Write File"); + JButton readFileButton = new JButton("Read File"); + JButton connectSocketButton = new JButton("Connect Socket"); + JButton frameButton = new JButton("Open Frame Without Warning Tag"); + JButton readSystemPropButton = new JButton("Read System Property"); + JButton printButton = new JButton("Print"); + JButton clipBoardButton = new JButton("Read Clipboard"); + + JTextField writePath = new JTextField( "/amd/ns/root/home/sbarnes/test.txt" ); + JTextField readPath = new JTextField("/amd/ns/root/home/sbarnes/test.txt"); + JTextField url = new JTextField("URL"); + JTextField port = new JTextField("port"); + JTextField systemProp = new JTextField("os.name"); + JTextField output = new JTextField(); + + //Construct the applet + public BadApplet() { + try { + //event handlers ****************************************************** + writeFileButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + writeFileButton_actionPerformed(e); + } + }); + readFileButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + readFileButton_actionPerformed(e); + } + }); + connectSocketButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + connectSocketButton_actionPerformed(e); + } + }); + frameButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + frameButton_actionPerformed(e); + } + }); + readSystemPropButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + readSystemPropButton_actionPerformed(e); + } + }); + printButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + printButton_actionPerformed(e); + } + }); + clipBoardButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + clipBoard_actionPerformed(e); + } + }); + + //do layout *********************************************************** + getContentPane().setLayout( new BorderLayout() ); + + FileSystemTests.setLayout( new FlowLayout( FlowLayout.LEFT ) ); + FileSystemTests.add( writeFileButton ); + FileSystemTests.add( writePath ); + FileSystemTests.add( readFileButton ); + FileSystemTests.add( readPath ); + + NetworkTests.setLayout( new FlowLayout( FlowLayout.LEFT ) ); + NetworkTests.add( connectSocketButton ); + NetworkTests.add( url ); + NetworkTests.add( port ); + + EnvironmentTests.setLayout( new FlowLayout( FlowLayout.LEFT ) ); + EnvironmentTests.add( frameButton ); + EnvironmentTests.add( readSystemPropButton ); + EnvironmentTests.add( systemProp ); + EnvironmentTests.add( printButton ); + EnvironmentTests.add( clipBoardButton ); + + tabs.add( FileSystemTests, "File System" ); + tabs.add( NetworkTests, "Network" ); + tabs.add( EnvironmentTests, "Environment" ); + + this.getContentPane().add( tabs, BorderLayout.CENTER ); + this.getContentPane().add( output, BorderLayout.SOUTH ); + } + catch(Exception e) { + e.printStackTrace(); + } + } + + public void paint( Graphics g ) + { + System.out.println( "graphics g = " + g ); + System.out.println( "clip area = " + g.getClip() ); + System.out.println( "bounds of the clip area = " + g.getClipBounds() ); + + super.paint( g ); + } + + //Initialize the applet + public void init() {} + + void writeFileButton_actionPerformed(ActionEvent e) { + try{ + PrintWriter writer = new PrintWriter(new FileOutputStream(writePath.getText())); + writer.println("Here is some text"); + writer.close(); + output.setText("Write was successful"); + } catch (Exception ex){output.setText(ex.getMessage());} + } + + void readSystemPropButton_actionPerformed(ActionEvent e) { + try{ + output.setText(System.getProperty(systemProp.getText())); + } catch (Exception ex){output.setText("Error getting prop: " + ex.getMessage());} + } + + void readFileButton_actionPerformed(ActionEvent e) { + try{ + BufferedReader reader = new BufferedReader(new FileReader(readPath.getText())); + output.setText("Read was successful: " + reader.readLine()); + } catch (Exception ex){output.setText(ex.getMessage());} + } + + void connectSocketButton_actionPerformed(ActionEvent e) { + try{ + Integer thePort = new Integer(port.getText()); + Socket socket = new Socket(url.getText(), thePort.intValue()); + socket.getOutputStream(); + output.setText("Socket connection successful"); + } catch (Exception ex){output.setText("Socket unsuccessfull: " + ex.getMessage());} + } + + void frameButton_actionPerformed(ActionEvent e) { + JFrame frame = new JFrame("Does this Frame have a warning sign"); + frame.setSize(200,200); + frame.show(); + if (frame.getWarningString() == null) + output.setText("No warning string in frame"); + else + output.setText(frame.getWarningString()); + } + + void clipBoard_actionPerformed(ActionEvent e) { + try { + Clipboard clip = Toolkit.getDefaultToolkit().getSystemClipboard(); + + Transferable trans = clip.getContents(null); + if (trans == null){ + output.setText("Clipboard is empty"); + return; + } + output.setText((String)trans.getTransferData(DataFlavor.stringFlavor)); + }catch(Exception ex){ex.getMessage();} + } + + void printButton_actionPerformed(ActionEvent e) { + try{ + JFrame testFrame = new JFrame("test"); + testFrame.getContentPane().add(this, BorderLayout.CENTER); + PrintJob printer = Toolkit.getDefaultToolkit().getPrintJob(testFrame, "Applet Print Test", null); + + if (printer == null){ + output.setText("PrintJob is null"); + return; + } + + Graphics g = printer.getGraphics(); + g.drawString("This is the applet print test", 50, 50); + g.dispose(); + printer.end(); + }catch(Exception ex){ex.getMessage();} + } + + //Main method + public static void main(String[] args) { + BadApplet applet = new BadApplet(); + + JFrame frame = new JFrame(); + frame.setDefaultCloseOperation( WindowConstants.DISPOSE_ON_CLOSE ); + frame.setTitle("Applet Frame"); + frame.getContentPane().add(applet, BorderLayout.CENTER); + frame.setSize(400,320); + frame.setVisible(true); + + applet.init(); + applet.start(); + } + +} diff --git a/khtml/java/tests/badapplets/applet.html b/khtml/java/tests/badapplets/applet.html new file mode 100644 index 000000000..fe9e47ef9 --- /dev/null +++ b/khtml/java/tests/badapplets/applet.html @@ -0,0 +1,14 @@ +<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> +<html> + <head> + <title>SwingSet demo</title> + </head> + + <body> + <h1>SwingSet demo</h1> + <applet code=BadApplet.class + archive="BadApplet.jar" + width=695 height=525> + </applet> + </body> +</html> diff --git a/khtml/java/tests/good_sites b/khtml/java/tests/good_sites new file mode 100644 index 000000000..f9699fe2a --- /dev/null +++ b/khtml/java/tests/good_sites @@ -0,0 +1,44 @@ +http Sites +--------------------------------------------------------- +http://java.sun.com/products/plugin/1.3.1_01a/demos/applets.html +www.soda.co.uk +java.sun.com +www.javaboutique.com +www.teledyn.com/fun/SaubleBeach (redraw problems, also in netscape) +www.fxapplets.com +www.quote.com/quotecom/livecharts/ +games.yahoo.com/games/klondike.html (security exception) +www.webtrac.co.za/cgi-bin/demo/demo.pl (classloader problem) +www.sodaplay.com/constructor/player.htm +www.sodaplay.com +www.dseffects.com/applets.html +www.controlzed.com +javaboutique.internet.com/Durius/ +www.chess.net/play/java +www.kmelektronik.de/root/index.html +rdufour.mytradecenter.com/applets/Tetris/Tetris.html +http://www.shiatsu-austria.at + +currently unavailable: +---------------------- +screening.nasdaq.com/screening/NASDAQSearch.asp + +unavailable: +------------ +www.indegocomputer.de +http://aktien.onvista.de/risk-return-map/ + +https Sites +--------------------------------------------------------- +https://brokerage-m3.consors.de/ConSors/ +https://homebanking.dvg-ka.de/045/index.html +https://spk-ihb.izb-hb.de/SPK_Deggendorf/index.html +https://www3.genodirekt.de/homebank.nsf/(rzbks)/xxq1050x?OpenDocument +https://banking.sonline.de/kreissparkasse-duesseldorf/ + +Security Test Sites +--------------------------------------------------------- +http://java.sun.com/sfaq/#examples +http://www.heise.de/ct/browsercheck/java.shtml +http://www.heise.de/ct/browsercheck/n4demo1.shtml + diff --git a/khtml/java/tests/testkjavaappletserver.cpp b/khtml/java/tests/testkjavaappletserver.cpp new file mode 100644 index 000000000..f0c5da19a --- /dev/null +++ b/khtml/java/tests/testkjavaappletserver.cpp @@ -0,0 +1,41 @@ +#include <kapplication.h> +#include <kcmdlineargs.h> +#include <kdebug.h> +#include <qstring.h> +#include <stdio.h> +#include <unistd.h> + +#include "java/kjavaappletserver.h" +#include "java/kjavaapplet.h" +#include "java/kjavaappletwidget.h" + +static KCmdLineOptions options[] = +{ + { "+[kdelibs_path]", "path to kdelibs directory", 0 }, + KCmdLineLastOption +}; + +int main(int argc, char **argv) +{ + KCmdLineArgs::init( argc, argv, "testKJASSever", "testKJASServer", "test program", "0.0" ); + + KCmdLineArgs::addCmdLineOptions( options ); + + KApplication app; + + QString path_to_kdelibs = "/build/wynnw/kde-src"; + + KJavaAppletWidget *a = new KJavaAppletWidget; + + a->show(); + + a->applet()->setBaseURL( "file:" + path_to_kdelibs + "/kdelibs/khtml/test/" ); + a->applet()->setAppletName( "Lake" ); + a->applet()->setAppletClass( "lake.class" ); + a->applet()->setParameter( "image", "konqi.gif" ); + + a->showApplet(); + a->applet()->start(); + + app.exec(); +} |