summaryrefslogtreecommitdiffstats
path: root/khtml/kmultipart
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commitce4a32fe52ef09d8f5ff1dd22c001110902b60a2 (patch)
tree5ac38a06f3dde268dc7927dc155896926aaf7012 /khtml/kmultipart
downloadtdelibs-ce4a32fe52ef09d8f5ff1dd22c001110902b60a2.tar.gz
tdelibs-ce4a32fe52ef09d8f5ff1dd22c001110902b60a2.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdelibs@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'khtml/kmultipart')
-rw-r--r--khtml/kmultipart/Makefile.am17
-rw-r--r--khtml/kmultipart/README16
-rw-r--r--khtml/kmultipart/kmultipart.cpp613
-rw-r--r--khtml/kmultipart/kmultipart.desktop73
-rw-r--r--khtml/kmultipart/kmultipart.h118
5 files changed, 837 insertions, 0 deletions
diff --git a/khtml/kmultipart/Makefile.am b/khtml/kmultipart/Makefile.am
new file mode 100644
index 000000000..bd79f89a5
--- /dev/null
+++ b/khtml/kmultipart/Makefile.am
@@ -0,0 +1,17 @@
+INCLUDES = -I$(top_srcdir)/khtml -I$(top_srcdir)/kio/httpfilter -I$(top_srcdir)/kutils $(all_includes)
+
+# These are not really libraries, but modules dynamically opened.
+# So they should be installed in kde_module_dir, which is usually $kde_prefix/lib/kde3
+kde_module_LTLIBRARIES = libkmultipart.la
+
+libkmultipart_la_SOURCES = kmultipart.cpp
+libkmultipart_la_LIBADD = $(LIB_KPARTS) $(top_builddir)/kio/httpfilter/libhttpfilter.la
+libkmultipart_la_DEPENDENCIES = $(LIB_KPARTS)
+libkmultipart_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN)
+
+# Automatically generate moc files
+METASOURCES = AUTO
+
+# Install the .desktop file into the kde_services directory
+kde_services_DATA = kmultipart.desktop
+
diff --git a/khtml/kmultipart/README b/khtml/kmultipart/README
new file mode 100644
index 000000000..c8a342d68
--- /dev/null
+++ b/khtml/kmultipart/README
@@ -0,0 +1,16 @@
+KMultiPart implements "server push" for KHTML/Konqueror:
+it handles the multipart/mixed and multipart/x-mixed-replace
+mimetype, embedding the appropriate component (part).
+
+Documentation at http://www.netscape.com/assist/net_sites/pushpull.html
+
+Typical real-world uses: webchats, webcams...
+
+Testcases:
+ http://stein.cshl.org/WWW/software/CGI/examples/
+
+TODO:
+* Use the new streaming API of KParts to pipe data into the part,
+the current code does that for KHTML only.
+* Change KHTML so that it embeds KMultiPart for images which send multipart/x-mixed-replace
+data.
diff --git a/khtml/kmultipart/kmultipart.cpp b/khtml/kmultipart/kmultipart.cpp
new file mode 100644
index 000000000..708bfc54f
--- /dev/null
+++ b/khtml/kmultipart/kmultipart.cpp
@@ -0,0 +1,613 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 David Faure <david@mandrakesoft.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 "kmultipart.h"
+
+#include <qvbox.h>
+#include <kinstance.h>
+#include <kmimetype.h>
+#include <klocale.h>
+#include <kio/job.h>
+#include <qfile.h>
+#include <ktempfile.h>
+#include <kmessagebox.h>
+#include <kparts/componentfactory.h>
+#include <kparts/genericfactory.h>
+#include <khtml_part.h>
+#include <unistd.h>
+#include <kxmlguifactory.h>
+#include <qtimer.h>
+
+typedef KParts::GenericFactory<KMultiPart> KMultiPartFactory; // factory for the part
+K_EXPORT_COMPONENT_FACTORY( libkmultipart /*library name*/, KMultiPartFactory )
+
+//#define DEBUG_PARSING
+
+class KLineParser
+{
+public:
+ KLineParser() {
+ m_lineComplete = false;
+ }
+ void addChar( char c, bool storeNewline ) {
+ if ( !storeNewline && c == '\r' )
+ return;
+ Q_ASSERT( !m_lineComplete );
+ if ( storeNewline || c != '\n' ) {
+ int sz = m_currentLine.size();
+ m_currentLine.resize( sz+1, QGArray::SpeedOptim );
+ m_currentLine[sz] = c;
+ }
+ if ( c == '\n' )
+ m_lineComplete = true;
+ }
+ bool isLineComplete() const {
+ return m_lineComplete;
+ }
+ QByteArray currentLine() const {
+ return m_currentLine;
+ }
+ void clearLine() {
+ Q_ASSERT( m_lineComplete );
+ reset();
+ }
+ void reset() {
+ m_currentLine.resize( 0, QGArray::SpeedOptim );
+ m_lineComplete = false;
+ }
+private:
+ QByteArray m_currentLine;
+ bool m_lineComplete; // true when ending with '\n'
+};
+
+/* testcase:
+ Content-type: multipart/mixed;boundary=ThisRandomString
+
+--ThisRandomString
+Content-type: text/plain
+
+Data for the first object.
+
+--ThisRandomString
+Content-type: text/plain
+
+Data for the second and last object.
+
+--ThisRandomString--
+*/
+
+
+KMultiPart::KMultiPart( QWidget *parentWidget, const char *widgetName,
+ QObject *parent, const char *name, const QStringList& )
+ : KParts::ReadOnlyPart( parent, name )
+{
+ m_filter = 0L;
+
+ setInstance( KMultiPartFactory::instance() );
+
+ QVBox *box = new QVBox( parentWidget, widgetName );
+ setWidget( box );
+
+ m_extension = new KParts::BrowserExtension( this );
+
+ // We probably need to use m_extension to get the urlArgs in openURL...
+
+ m_part = 0L;
+ m_isHTMLPart = false;
+ m_job = 0L;
+ m_lineParser = new KLineParser;
+ m_tempFile = 0L;
+
+ m_timer = new QTimer( this );
+ connect( m_timer, SIGNAL( timeout() ), this, SLOT( slotProgressInfo() ) );
+}
+
+KMultiPart::~KMultiPart()
+{
+ // important: delete the nested part before the part or qobject destructor runs.
+ // we now delete the nested part which deletes the part's widget which makes
+ // _OUR_ m_widget 0 which in turn avoids our part destructor to delete the
+ // widget ;-)
+ // ### additional note: it _can_ be that the part has been deleted before:
+ // when we're in a html frameset and the view dies first, then it will also
+ // kill the htmlpart
+ if ( m_part )
+ delete static_cast<KParts::ReadOnlyPart *>( m_part );
+ delete m_job;
+ delete m_lineParser;
+ if ( m_tempFile ) {
+ m_tempFile->setAutoDelete( true );
+ delete m_tempFile;
+ }
+ delete m_filter;
+ m_filter = 0L;
+}
+
+
+void KMultiPart::startHeader()
+{
+ m_bParsingHeader = true; // we expect a header to come first
+ m_bGotAnyHeader = false;
+ m_gzip = false;
+ // just to be sure for now
+ delete m_filter;
+ m_filter = 0L;
+}
+
+
+bool KMultiPart::openURL( const KURL &url )
+{
+ m_url = url;
+ m_lineParser->reset();
+ startHeader();
+
+ KParts::URLArgs args = m_extension->urlArgs();
+ //m_mimeType = args.serviceType;
+
+ // Hmm, args.reload is set to true when reloading, but this doesn't seem to be enough...
+ // I get "HOLD: Reusing held slave for <url>", and the old data
+
+ m_job = KIO::get( url, args.reload, false );
+
+ emit started( 0 /*m_job*/ ); // don't pass the job, it would interfer with our own infoMessage
+
+ connect( m_job, SIGNAL( result( KIO::Job * ) ),
+ this, SLOT( slotJobFinished( KIO::Job * ) ) );
+ connect( m_job, SIGNAL( data( KIO::Job *, const QByteArray & ) ),
+ this, SLOT( slotData( KIO::Job *, const QByteArray & ) ) );
+
+ m_numberOfFrames = 0;
+ m_numberOfFramesSkipped = 0;
+ m_totalNumberOfFrames = 0;
+ m_qtime.start();
+ m_timer->start( 1000 ); //1s
+
+ return true;
+}
+
+// Yes, libkdenetwork's has such a parser already (MultiPart),
+// but it works on the complete string, expecting the whole data to be available....
+// The version here is asynchronous.
+void KMultiPart::slotData( KIO::Job *job, const QByteArray &data )
+{
+ if (m_boundary.isNull())
+ {
+ QString tmp = job->queryMetaData("media-boundary");
+ kdDebug() << "Got Boundary from kio-http '" << tmp << "'" << endl;
+ if ( !tmp.isEmpty() ) {
+ if (tmp.startsWith("--"))
+ m_boundary = tmp.latin1();
+ else
+ m_boundary = QCString("--")+tmp.latin1();
+ m_boundaryLength = m_boundary.length();
+ }
+ }
+ // Append to m_currentLine until eol
+ for ( uint i = 0; i < data.size() ; ++i )
+ {
+ // Store char. Skip if '\n' and currently parsing a header.
+ m_lineParser->addChar( data[i], !m_bParsingHeader );
+ if ( m_lineParser->isLineComplete() )
+ {
+ QByteArray lineData = m_lineParser->currentLine();
+#ifdef DEBUG_PARSING
+ kdDebug() << "lineData.size()=" << lineData.size() << endl;
+#endif
+ QCString line( lineData.data(), lineData.size()+1 ); // deep copy
+ // 0-terminate the data, but only for the line-based tests below
+ // We want to keep the raw data in case it ends up in sendData()
+ int sz = line.size();
+ if ( sz > 0 )
+ line[sz-1] = '\0';
+#ifdef DEBUG_PARSING
+ kdDebug() << "[" << m_bParsingHeader << "] line='" << line << "'" << endl;
+#endif
+ if ( m_bParsingHeader )
+ {
+ if ( !line.isEmpty() )
+ m_bGotAnyHeader = true;
+ if ( m_boundary.isNull() )
+ {
+ if ( !line.isEmpty() ) {
+#ifdef DEBUG_PARSING
+ kdDebug() << "Boundary is " << line << endl;
+#endif
+ m_boundary = line;
+ m_boundaryLength = m_boundary.length();
+ }
+ }
+ else if ( !qstrnicmp( line.data(), "Content-Encoding:", 17 ) )
+ {
+ QString encoding = QString::fromLatin1(line.data()+17).stripWhiteSpace().lower();
+ if (encoding == "gzip" || encoding == "x-gzip") {
+ m_gzip = true;
+ } else {
+ kdDebug() << "FIXME: unhandled encoding type in KMultiPart: " << encoding << endl;
+ }
+ }
+ // parse Content-Type
+ else if ( !qstrnicmp( line.data(), "Content-Type:", 13 ) )
+ {
+ Q_ASSERT( m_nextMimeType.isNull() );
+ m_nextMimeType = QString::fromLatin1( line.data() + 14 ).stripWhiteSpace();
+ int semicolon = m_nextMimeType.find( ';' );
+ if ( semicolon != -1 )
+ m_nextMimeType = m_nextMimeType.left( semicolon );
+ kdDebug() << "m_nextMimeType=" << m_nextMimeType << endl;
+ }
+ // Empty line, end of headers (if we had any header line before)
+ else if ( line.isEmpty() && m_bGotAnyHeader )
+ {
+ m_bParsingHeader = false;
+#ifdef DEBUG_PARSING
+ kdDebug() << "end of headers" << endl;
+#endif
+ startOfData();
+ }
+ // First header (when we know it from kio_http)
+ else if ( line == m_boundary )
+ ; // nothing to do
+ else if ( !line.isEmpty() ) // this happens with e.g. Set-Cookie:
+ kdDebug() << "Ignoring header " << line << endl;
+ } else {
+ if ( !qstrncmp( line, m_boundary, m_boundaryLength ) )
+ {
+#ifdef DEBUG_PARSING
+ kdDebug() << "boundary found!" << endl;
+ kdDebug() << "after it is " << line.data() + m_boundaryLength << endl;
+#endif
+ // Was it the very last boundary ?
+ if ( !qstrncmp( line.data() + m_boundaryLength, "--", 2 ) )
+ {
+#ifdef DEBUG_PARSING
+ kdDebug() << "Completed!" << endl;
+#endif
+ endOfData();
+ emit completed();
+ } else
+ {
+ char nextChar = *(line.data() + m_boundaryLength);
+#ifdef DEBUG_PARSING
+ kdDebug() << "KMultiPart::slotData nextChar='" << nextChar << "'" << endl;
+#endif
+ if ( nextChar == '\n' || nextChar == '\r' ) {
+ endOfData();
+ startHeader();
+ }
+ else {
+ // otherwise, false hit, it has trailing stuff
+ sendData( lineData );
+ }
+ }
+ } else {
+ // send to part
+ sendData( lineData );
+ }
+ }
+ m_lineParser->clearLine();
+ }
+ }
+}
+
+void KMultiPart::setPart( const QString& mimeType )
+{
+ KXMLGUIFactory *guiFactory = factory();
+ if ( guiFactory ) // seems to be 0 when restoring from SM
+ guiFactory->removeClient( this );
+ kdDebug() << "KMultiPart::setPart " << mimeType << endl;
+ delete m_part;
+ // Try to find an appropriate viewer component
+ m_part = KParts::ComponentFactory::createPartInstanceFromQuery<KParts::ReadOnlyPart>
+ ( m_mimeType, QString::null, widget(), 0L, this, 0L );
+ if ( !m_part ) {
+ // TODO launch external app
+ KMessageBox::error( widget(), i18n("No handler found for %1!").arg(m_mimeType) );
+ return;
+ }
+ // By making the part a child XMLGUIClient of ours, we get its GUI merged in.
+ insertChildClient( m_part );
+ m_part->widget()->show();
+
+ connect( m_part, SIGNAL( completed() ),
+ this, SLOT( slotPartCompleted() ) );
+
+ m_isHTMLPart = ( mimeType == "text/html" );
+ KParts::BrowserExtension* childExtension = KParts::BrowserExtension::childObject( m_part );
+
+ if ( childExtension )
+ {
+
+ // Forward signals from the part's browser extension
+ // this is very related (but not exactly like) KHTMLPart::processObjectRequest
+
+ connect( childExtension, SIGNAL( openURLNotify() ),
+ m_extension, SIGNAL( openURLNotify() ) );
+
+ connect( childExtension, SIGNAL( openURLRequestDelayed( const KURL &, const KParts::URLArgs & ) ),
+ m_extension, SIGNAL( openURLRequest( const KURL &, const KParts::URLArgs & ) ) );
+
+ connect( childExtension, SIGNAL( createNewWindow( const KURL &, const KParts::URLArgs & ) ),
+ m_extension, SIGNAL( createNewWindow( const KURL &, const KParts::URLArgs & ) ) );
+ connect( childExtension, SIGNAL( createNewWindow( const KURL &, const KParts::URLArgs &, const KParts::WindowArgs &, KParts::ReadOnlyPart *& ) ),
+ m_extension, SIGNAL( createNewWindow( const KURL &, const KParts::URLArgs & , const KParts::WindowArgs &, KParts::ReadOnlyPart *&) ) );
+
+ // Keep in sync with khtml_part.cpp
+ connect( childExtension, SIGNAL( popupMenu( const QPoint &, const KFileItemList & ) ),
+ m_extension, SIGNAL( popupMenu( const QPoint &, const KFileItemList & ) ) );
+ connect( childExtension, SIGNAL( popupMenu( KXMLGUIClient *, const QPoint &, const KFileItemList & ) ),
+ m_extension, SIGNAL( popupMenu( KXMLGUIClient *, const QPoint &, const KFileItemList & ) ) );
+ connect( childExtension, SIGNAL( popupMenu( KXMLGUIClient *, const QPoint &, const KFileItemList &, const KParts::URLArgs &, KParts::BrowserExtension::PopupFlags ) ),
+ m_extension, SIGNAL( popupMenu( KXMLGUIClient *, const QPoint &, const KFileItemList &, const KParts::URLArgs &, KParts::BrowserExtension::PopupFlags ) ) );
+ connect( childExtension, SIGNAL( popupMenu( const QPoint &, const KURL &, const QString &, mode_t ) ),
+ m_extension, SIGNAL( popupMenu( const QPoint &, const KURL &, const QString &, mode_t ) ) );
+ connect( childExtension, SIGNAL( popupMenu( KXMLGUIClient *, const QPoint &, const KURL &, const QString &, mode_t ) ),
+ m_extension, SIGNAL( popupMenu( KXMLGUIClient *, const QPoint &, const KURL &, const QString &, mode_t ) ) );
+ connect( childExtension, SIGNAL( popupMenu( KXMLGUIClient *, const QPoint &, const KURL &, const KParts::URLArgs &, KParts::BrowserExtension::PopupFlags, mode_t ) ),
+ m_extension, SIGNAL( popupMenu( KXMLGUIClient *, const QPoint &, const KURL &, const KParts::URLArgs &, KParts::BrowserExtension::PopupFlags, mode_t ) ) );
+
+
+ if ( m_isHTMLPart )
+ connect( childExtension, SIGNAL( infoMessage( const QString & ) ),
+ m_extension, SIGNAL( infoMessage( const QString & ) ) );
+ // For non-HTML we prefer to show our infoMessage ourselves.
+
+ childExtension->setBrowserInterface( m_extension->browserInterface() );
+
+ connect( childExtension, SIGNAL( enableAction( const char *, bool ) ),
+ m_extension, SIGNAL( enableAction( const char *, bool ) ) );
+ connect( childExtension, SIGNAL( setLocationBarURL( const QString& ) ),
+ m_extension, SIGNAL( setLocationBarURL( const QString& ) ) );
+ connect( childExtension, SIGNAL( setIconURL( const KURL& ) ),
+ m_extension, SIGNAL( setIconURL( const KURL& ) ) );
+ connect( childExtension, SIGNAL( loadingProgress( int ) ),
+ m_extension, SIGNAL( loadingProgress( int ) ) );
+ if ( m_isHTMLPart ) // for non-HTML we have our own
+ connect( childExtension, SIGNAL( speedProgress( int ) ),
+ m_extension, SIGNAL( speedProgress( int ) ) );
+ connect( childExtension, SIGNAL( selectionInfo( const KFileItemList& ) ),
+ m_extension, SIGNAL( selectionInfo( const KFileItemList& ) ) );
+ connect( childExtension, SIGNAL( selectionInfo( const QString& ) ),
+ m_extension, SIGNAL( selectionInfo( const QString& ) ) );
+ connect( childExtension, SIGNAL( selectionInfo( const KURL::List& ) ),
+ m_extension, SIGNAL( selectionInfo( const KURL::List& ) ) );
+ connect( childExtension, SIGNAL( mouseOverInfo( const KFileItem* ) ),
+ m_extension, SIGNAL( mouseOverInfo( const KFileItem* ) ) );
+ connect( childExtension, SIGNAL( moveTopLevelWidget( int, int ) ),
+ m_extension, SIGNAL( moveTopLevelWidget( int, int ) ) );
+ connect( childExtension, SIGNAL( resizeTopLevelWidget( int, int ) ),
+ m_extension, SIGNAL( resizeTopLevelWidget( int, int ) ) );
+ }
+
+ m_partIsLoading = false;
+ // Load the part's plugins too.
+ // ###### This is a hack. The bug is that KHTMLPart doesn't load its plugins
+ // if className != "Browser/View".
+ loadPlugins( this, m_part, m_part->instance() );
+ // Get the part's GUI to appear
+ if ( guiFactory )
+ guiFactory->addClient( this );
+}
+
+void KMultiPart::startOfData()
+{
+ kdDebug() << "KMultiPart::startOfData" << endl;
+ Q_ASSERT( !m_nextMimeType.isNull() );
+ if( m_nextMimeType.isNull() )
+ return;
+
+ if ( m_gzip )
+ {
+ m_filter = new HTTPFilterGZip;
+ connect( m_filter, SIGNAL( output( const QByteArray& ) ), this, SLOT( reallySendData( const QByteArray& ) ) );
+ }
+
+ if ( m_mimeType != m_nextMimeType )
+ {
+ // Need to switch parts (or create the initial one)
+ m_mimeType = m_nextMimeType;
+ setPart( m_mimeType );
+ }
+ Q_ASSERT( m_part );
+ // Pass URLArgs (e.g. reload)
+ KParts::BrowserExtension* childExtension = KParts::BrowserExtension::childObject( m_part );
+ if ( childExtension )
+ childExtension->setURLArgs( m_extension->urlArgs() );
+
+ m_nextMimeType = QString::null;
+ if ( m_tempFile ) {
+ m_tempFile->setAutoDelete( true );
+ delete m_tempFile;
+ m_tempFile = 0;
+ }
+ if ( m_isHTMLPart )
+ {
+ KHTMLPart* htmlPart = static_cast<KHTMLPart *>( static_cast<KParts::ReadOnlyPart *>( m_part ) );
+ htmlPart->begin( url() );
+ }
+ else
+ {
+ // ###### TODO use a QByteArray and a data: URL instead
+ m_tempFile = new KTempFile;
+ }
+}
+
+void KMultiPart::sendData( const QByteArray& line )
+{
+ if ( m_filter )
+ {
+ m_filter->slotInput( line );
+ }
+ else
+ {
+ reallySendData( line );
+ }
+}
+
+void KMultiPart::reallySendData( const QByteArray& line )
+{
+ if ( m_isHTMLPart )
+ {
+ KHTMLPart* htmlPart = static_cast<KHTMLPart *>( static_cast<KParts::ReadOnlyPart *>( m_part ) );
+ htmlPart->write( line.data(), line.size() );
+ }
+ else if ( m_tempFile )
+ {
+ m_tempFile->file()->writeBlock( line.data(), line.size() );
+ }
+}
+
+void KMultiPart::endOfData()
+{
+ Q_ASSERT( m_part );
+ if ( m_isHTMLPart )
+ {
+ KHTMLPart* htmlPart = static_cast<KHTMLPart *>( static_cast<KParts::ReadOnlyPart *>( m_part ) );
+ htmlPart->end();
+ } else if ( m_tempFile )
+ {
+ m_tempFile->close();
+ if ( m_partIsLoading )
+ {
+ // The part is still loading the last data! Let it proceed then
+ // Otherwise we'd keep cancelling it, and nothing would ever show up...
+ kdDebug() << "KMultiPart::endOfData part isn't ready, skipping frame" << endl;
+ ++m_numberOfFramesSkipped;
+ m_tempFile->setAutoDelete( true );
+ }
+ else
+ {
+ kdDebug() << "KMultiPart::endOfData opening " << m_tempFile->name() << endl;
+ KURL url;
+ url.setPath( m_tempFile->name() );
+ m_partIsLoading = true;
+ (void) m_part->openURL( url );
+ }
+ delete m_tempFile;
+ m_tempFile = 0L;
+ }
+}
+
+void KMultiPart::slotPartCompleted()
+{
+ if ( !m_isHTMLPart )
+ {
+ Q_ASSERT( m_part );
+ // Delete temp file used by the part
+ Q_ASSERT( m_part->url().isLocalFile() );
+ kdDebug() << "slotPartCompleted deleting " << m_part->url().path() << endl;
+ (void) unlink( QFile::encodeName( m_part->url().path() ) );
+ m_partIsLoading = false;
+ ++m_numberOfFrames;
+ // Do not emit completed from here.
+ }
+}
+
+bool KMultiPart::closeURL()
+{
+ m_timer->stop();
+ if ( m_part )
+ return m_part->closeURL();
+ return true;
+}
+
+void KMultiPart::guiActivateEvent( KParts::GUIActivateEvent * )
+{
+ // Not public!
+ //if ( m_part )
+ // m_part->guiActivateEvent( e );
+}
+
+void KMultiPart::slotJobFinished( KIO::Job *job )
+{
+ if ( job->error() )
+ {
+ // TODO use khtml's error:// scheme
+ job->showErrorDialog();
+ emit canceled( job->errorString() );
+ }
+ else
+ {
+ /*if ( m_khtml->view()->contentsY() == 0 )
+ {
+ KParts::URLArgs args = m_ext->urlArgs();
+ m_khtml->view()->setContentsPos( args.xOffset, args.yOffset );
+ }*/
+
+ emit completed();
+
+ //QTimer::singleShot( 0, this, SLOT( updateWindowCaption() ) );
+ }
+ m_job = 0L;
+}
+
+void KMultiPart::slotProgressInfo()
+{
+ int time = m_qtime.elapsed();
+ if ( !time ) return;
+ if ( m_totalNumberOfFrames == m_numberOfFrames + m_numberOfFramesSkipped )
+ return; // No change, don't overwrite statusbar messages if any
+ //kdDebug() << m_numberOfFrames << " in " << time << " milliseconds" << endl;
+ QString str( "%1 frames per second, %2 frames skipped per second" );
+ str = str.arg( 1000.0 * (double)m_numberOfFrames / (double)time );
+ str = str.arg( 1000.0 * (double)m_numberOfFramesSkipped / (double)time );
+ m_totalNumberOfFrames = m_numberOfFrames + m_numberOfFramesSkipped;
+ //kdDebug() << str << endl;
+ emit m_extension->infoMessage( str );
+}
+
+KAboutData* KMultiPart::createAboutData()
+{
+ KAboutData* aboutData = new KAboutData( "kmultipart", I18N_NOOP("KMultiPart"),
+ "0.1",
+ I18N_NOOP( "Embeddable component for multipart/mixed" ),
+ KAboutData::License_GPL,
+ "(c) 2001, David Faure <david@mandrakesoft.com>");
+ return aboutData;
+}
+
+#if 0
+KMultiPartBrowserExtension::KMultiPartBrowserExtension( KMultiPart *parent, const char *name )
+ : KParts::BrowserExtension( parent, name )
+{
+ m_imgPart = parent;
+}
+
+int KMultiPartBrowserExtension::xOffset()
+{
+ return m_imgPart->doc()->view()->contentsX();
+}
+
+int KMultiPartBrowserExtension::yOffset()
+{
+ return m_imgPart->doc()->view()->contentsY();
+}
+
+void KMultiPartBrowserExtension::print()
+{
+ static_cast<KHTMLPartBrowserExtension *>( m_imgPart->doc()->browserExtension() )->print();
+}
+
+void KMultiPartBrowserExtension::reparseConfiguration()
+{
+ static_cast<KHTMLPartBrowserExtension *>( m_imgPart->doc()->browserExtension() )->reparseConfiguration();
+ m_imgPart->doc()->setAutoloadImages( true );
+}
+#endif
+
+#include "kmultipart.moc"
diff --git a/khtml/kmultipart/kmultipart.desktop b/khtml/kmultipart/kmultipart.desktop
new file mode 100644
index 000000000..6419edac6
--- /dev/null
+++ b/khtml/kmultipart/kmultipart.desktop
@@ -0,0 +1,73 @@
+[Desktop Entry]
+Type=Service
+MimeType=multipart/mixed;multipart/x-mixed-replace
+Name=Embeddable Component for multipart/mixed
+Name[af]=Inlegbare Komponent vir multideel/gemeng
+Name[be]=Унутраны кампанент для multipart/mixed
+Name[bg]=Вграден компонент за преглед на съобщения multipart/mixed
+Name[bn]=multipart/mixed-এর জন্য অভ্যন্তরীণ উপাদান
+Name[bs]=Ugradiva komponenta za multipart/mixed
+Name[ca]=Component encastable per a multipart/mescla
+Name[cs]=Pohltitelné komponenty pro 'multipart/mixed'
+Name[csb]=Składowi kòmpònent dlô multipart/mixed
+Name[da]=Indlejrbar komponent for multipart/mixed
+Name[de]=Einbettungsfähige Komponente für MIME-Typ multipart/mixed
+Name[el]=Στοιχείο που μπορεί να ενσωματωθεί για multipart/mixed
+Name[eo]=Enplantebla komponanto por plurparto/mikso
+Name[es]=Componente empotrado para multiparte/mezcla
+Name[et]=Multipart/mixed põimitav komponent
+Name[eu]=Kapsulatutako zati-anitzen/nahastuen osagaia
+Name[fa]=مؤلفۀ نهفته برای چند بخش/مخلوط
+Name[fi]=Upotettava komponentti multipart/mixed-hallintaan
+Name[fr]=Composant intégrable pour le type multipart / mixed
+Name[fy]=Yn te sluten komponint foar mulipart/mixed
+Name[ga]=Comhpháirt Inleabaithe le haghaidh multipart/mixed
+Name[gl]=Compoñente incrustábel para multpart/mixed
+Name[he]=רכיב בר־הטבעה עבור מרובה חלקים\מעורב
+Name[hi]=मल्टीपार्ट/मिक्स्ड के लिए अंतर्निहित करने योग्य घटक
+Name[hr]=Ugradiva komponenta za multipart/mixed
+Name[hu]=Beágyazható komponens a multipart/mixed adattípushoz
+Name[id]=Komponen Tersisipkan untuk multipart/mixed
+Name[is]=Ívefjanleg eining fyrir MIME multipart/mixed
+Name[it]=Componente integrabile per multipart/mixed
+Name[ja]=マルチパート/混合 の埋め込み可能なコンポーネント
+Name[ka]=ჩადგმადი კომპონენტი მრავალნაწილიანი/შერეულისთვის
+Name[kk]=multipart/mixed тіркеме үшін ендірілетін компоненті
+Name[km]=សមាសភាគ​ដែល​អាច​បង្កប់ សម្រាប់​ផ្នែកជាច្រើន/លាយ
+Name[lb]=Abettbar Komponent fir de MIME-Genre multipart/mixed
+Name[lt]=Įdedamas komponentas multipart/mixed tipui
+Name[lv]=Iegulstama komponente priekš multipart/mixed
+Name[mk]=Вгнездлива компонента за multipart/mixed
+Name[ms]=Komponen boleh serta untuk pelbagai bahagian/bercampur
+Name[nb]=Innebyggbar komponent for multipart/mixed
+Name[nds]=Inbettbor Komponent för multipart/mixed
+Name[ne]=बहुभाग/मिश्रणका लागि सम्मिलित गर्न सकिने अवयव
+Name[nl]=Inbedbaar component voor multipart/mixed
+Name[nn]=Inkluderbar komponent for multipart/mixed
+Name[pa]=ਬਹੁਭਾਗੀ/ਰਲਵੇਂ ਲਈ ਸ਼ਾਮਿਲ ਭਾਗ
+Name[pl]=Moduł składowy dla multipart/mixed
+Name[pt]=Componente Embebido para multipart/mixed
+Name[pt_BR]=Componente embutido para multipart/mixed
+Name[ro]=Componentă înglobată pentru tip MIME multipart/mixed
+Name[ru]=Встраиваемый компонент для вложений
+Name[rw]=Inyangingo ishyirwamo y'ibicebyinshi/ibivanzwe
+Name[se]=Vuojuhanláhkái oassi multipart/mixed:a várás
+Name[sk]=Vložiteľný komponent pre multipart/mixed
+Name[sl]=Vgradljiva komponenta za multipart/mixed
+Name[sr]=Уградива компонента за „multipart/mixed“
+Name[sr@Latn]=Ugradiva komponenta za „multipart/mixed“
+Name[sv]=Inbyggbar komponent för multipart/mixed
+Name[ta]=பலப்பகுதி/கலக்கப்பட்ட பகுதிக்குரிய உட்பொதிந்த பகுதி
+Name[te]=మల్టిపార్ట్/మిక్స్డ్ కొరకు పొదిగిన అంశం
+Name[tg]=Компоненти дарунсохт барои қисматҳои зиёд/омезишшуда
+Name[th]=ส่วนประกอบที่ฝังตัวได้สำหรับมัลติพาร์ต/ผสม
+Name[tr]=Çok parçalı/karışık için Gömülebilir Bileşen
+Name[uk]=Вбудовна компонента для повідомлень multipart/mixed
+Name[uz]=Multipart/mixed uchun ichiga oʻrnatib boʻladigan komponent
+Name[uz@cyrillic]=Multipart/mixed учун ичига ўрнатиб бўладиган компонент
+Name[vi]=Thành phần có khả năng nhúng cho dạng thức thư đa phần/đã trộn (multipart/mixed).
+Name[zh_CN]=可嵌入的 multipart/mixed 组件
+Name[zh_HK]=multipart/mixed 的可嵌入元件
+Name[zh_TW]=multipart/mixed 的可嵌入元件
+ServiceTypes=KParts/ReadOnlyPart
+X-KDE-Library=libkmultipart
diff --git a/khtml/kmultipart/kmultipart.h b/khtml/kmultipart/kmultipart.h
new file mode 100644
index 000000000..b0633a64d
--- /dev/null
+++ b/khtml/kmultipart/kmultipart.h
@@ -0,0 +1,118 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 David Faure <david@mandrakesoft.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 __kmultipart_h__
+#define __kmultipart_h__
+
+#include <httpfilter/httpfilter.h>
+
+#include <kparts/part.h>
+#include <kparts/factory.h>
+#include <kparts/browserextension.h>
+#include <kaboutdata.h>
+#include <qdatetime.h>
+
+class KHTMLPart;
+class KInstance;
+class KTempFile;
+class KLineParser;
+
+/**
+ * http://www.netscape.com/assist/net_sites/pushpull.html
+ */
+class KMultiPart : public KParts::ReadOnlyPart
+{
+ Q_OBJECT
+public:
+ KMultiPart( QWidget *parentWidget, const char *widgetName,
+ QObject *parent, const char *name, const QStringList& );
+ virtual ~KMultiPart();
+
+ virtual bool openFile() { return false; }
+ virtual bool openURL( const KURL &url );
+
+ virtual bool closeURL();
+
+ static KAboutData* createAboutData();
+
+protected:
+ virtual void guiActivateEvent( KParts::GUIActivateEvent *e );
+ void setPart( const QString& mimeType );
+
+ void startOfData();
+ void sendData( const QByteArray& line );
+ void endOfData();
+
+private slots:
+ void reallySendData( const QByteArray& line );
+ //void slotPopupMenu( KXMLGUIClient *cl, const QPoint &pos, const KURL &u, const QString &mime, mode_t mode );
+ void slotJobFinished( KIO::Job *job );
+ void slotData( KIO::Job *, const QByteArray & );
+ //void updateWindowCaption();
+
+ void slotPartCompleted();
+
+ void startHeader();
+
+ void slotProgressInfo();
+
+private:
+ KParts::BrowserExtension* m_extension;
+ QGuardedPtr<KParts::ReadOnlyPart> m_part;
+ bool m_isHTMLPart;
+ bool m_partIsLoading;
+ KIO::Job* m_job;
+ QCString m_boundary;
+ int m_boundaryLength;
+ QString m_mimeType; // the one handled by m_part - store the kservice instead?
+ QString m_nextMimeType; // while parsing headers
+ KTempFile* m_tempFile;
+ KLineParser* m_lineParser;
+ bool m_bParsingHeader;
+ bool m_bGotAnyHeader;
+ bool m_gzip;
+ HTTPFilterBase *m_filter;
+ // Speed measurements
+ long m_totalNumberOfFrames;
+ long m_numberOfFrames;
+ long m_numberOfFramesSkipped;
+ QTime m_qtime;
+ QTimer* m_timer;
+};
+
+#if 0
+class KMultiPartBrowserExtension : public KParts::BrowserExtension
+{
+ //Q_OBJECT
+public:
+ KMultiPartBrowserExtension( KMultiPart *parent, const char *name = 0 );
+
+ virtual int xOffset();
+ virtual int yOffset();
+
+//protected slots:
+ void print();
+ void reparseConfiguration();
+
+private:
+ KMultiPart *m_imgPart;
+};
+#endif
+
+#endif