khtml Library API Documentation

kmultipart.cpp

00001 /* This file is part of the KDE project 00002 Copyright (C) 2002 David Faure <david@mandrakesoft.com> 00003 00004 This library is free software; you can redistribute it and/or 00005 modify it under the terms of the GNU Library General Public 00006 License as published by the Free Software Foundation; either 00007 version 2 of the License, or (at your option) any later version. 00008 00009 This library is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 Library General Public License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to 00016 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00017 Boston, MA 02111-1307, USA. 00018 */ 00019 00020 #include "kmultipart.h" 00021 00022 #include <qvbox.h> 00023 #include <kinstance.h> 00024 #include <kmimetype.h> 00025 #include <klocale.h> 00026 #include <kio/job.h> 00027 #include <qfile.h> 00028 #include <ktempfile.h> 00029 #include <kmessagebox.h> 00030 #include <kparts/componentfactory.h> 00031 #include <kparts/genericfactory.h> 00032 #include <khtml_part.h> 00033 #include <unistd.h> 00034 #include <kxmlguifactory.h> 00035 00036 typedef KParts::GenericFactory<KMultiPart> KMultiPartFactory; // factory for the part 00037 K_EXPORT_COMPONENT_FACTORY( libkmultipart /*library name*/, KMultiPartFactory ) 00038 00039 //#define DEBUG_PARSING 00040 00041 class KLineParser 00042 { 00043 public: 00044 KLineParser() { 00045 m_lineComplete = false; 00046 } 00047 void addChar( char c, bool storeNewline ) { 00048 if ( !storeNewline && c == '\r' ) 00049 return; 00050 Q_ASSERT( !m_lineComplete ); 00051 if ( storeNewline || c != '\n' ) { 00052 int sz = m_currentLine.size(); 00053 m_currentLine.resize( sz+1, QGArray::SpeedOptim ); 00054 m_currentLine[sz] = c; 00055 } 00056 if ( c == '\n' ) 00057 m_lineComplete = true; 00058 } 00059 bool isLineComplete() const { 00060 return m_lineComplete; 00061 } 00062 QByteArray currentLine() const { 00063 return m_currentLine; 00064 } 00065 void clearLine() { 00066 Q_ASSERT( m_lineComplete ); 00067 reset(); 00068 } 00069 void reset() { 00070 m_currentLine.resize( 0, QGArray::SpeedOptim ); 00071 m_lineComplete = false; 00072 } 00073 private: 00074 QByteArray m_currentLine; 00075 bool m_lineComplete; // true when ending with '\n' 00076 }; 00077 00078 /* testcase: 00079 Content-type: multipart/mixed;boundary=ThisRandomString 00080 00081 --ThisRandomString 00082 Content-type: text/plain 00083 00084 Data for the first object. 00085 00086 --ThisRandomString 00087 Content-type: text/plain 00088 00089 Data for the second and last object. 00090 00091 --ThisRandomString-- 00092 */ 00093 00094 00095 KMultiPart::KMultiPart( QWidget *parentWidget, const char *widgetName, 00096 QObject *parent, const char *name, const QStringList& ) 00097 : KParts::ReadOnlyPart( parent, name ) 00098 { 00099 m_filter = 0L; 00100 00101 setInstance( KMultiPartFactory::instance() ); 00102 00103 QVBox *box = new QVBox( parentWidget, widgetName ); 00104 setWidget( box ); 00105 00106 m_extension = new KParts::BrowserExtension( this ); 00107 00108 // We probably need to use m_extension to get the urlArgs in openURL... 00109 00110 m_part = 0L; 00111 m_job = 0L; 00112 m_lineParser = new KLineParser; 00113 m_tempFile = 0L; 00114 } 00115 00116 KMultiPart::~KMultiPart() 00117 { 00118 // important: delete the nested part before the part or qobject destructor runs. 00119 // we now delete the nested part which deletes the part's widget which makes 00120 // _OUR_ m_widget 0 which in turn avoids our part destructor to delete the 00121 // widget ;-) 00122 // ### additional note: it _can_ be that the part has been deleted before: 00123 // when we're in a html frameset and the view dies first, then it will also 00124 // kill the htmlpart 00125 if ( m_part ) 00126 delete static_cast<KParts::ReadOnlyPart *>( m_part ); 00127 delete m_job; 00128 delete m_lineParser; 00129 delete m_tempFile; 00130 delete m_filter; 00131 m_filter = 0L; 00132 } 00133 00134 00135 void KMultiPart::startHeader() 00136 { 00137 m_bParsingHeader = true; // we expect a header to come first 00138 m_bGotAnyHeader = false; 00139 m_gzip = false; 00140 // just to be sure for now 00141 delete m_filter; 00142 m_filter = 0L; 00143 } 00144 00145 00146 bool KMultiPart::openURL( const KURL &url ) 00147 { 00148 m_url = url; 00149 m_lineParser->reset(); 00150 startHeader(); 00151 00152 KParts::URLArgs args = m_extension->urlArgs(); 00153 //m_mimeType = args.serviceType; 00154 00155 // Hmm, args.reload is set to true when reloading, but this doesn't seem to be enough... 00156 // I get "HOLD: Reusing held slave for <url>", and the old data 00157 00158 m_job = KIO::get( url, args.reload, false ); 00159 00160 emit started( m_job ); 00161 00162 connect( m_job, SIGNAL( result( KIO::Job * ) ), 00163 this, SLOT( slotJobFinished( KIO::Job * ) ) ); 00164 connect( m_job, SIGNAL( data( KIO::Job *, const QByteArray & ) ), 00165 this, SLOT( slotData( KIO::Job *, const QByteArray & ) ) ); 00166 00167 return true; 00168 } 00169 00170 // Yes, libkdenetwork's has such a parser already (MultiPart), 00171 // but it works on the complete string, expecting the whole data to be available.... 00172 // The version here is asynchronous. 00173 void KMultiPart::slotData( KIO::Job *job, const QByteArray &data ) 00174 { 00175 if (m_boundary.isNull()) 00176 { 00177 QString tmp = job->queryMetaData("media-boundary"); 00178 kdDebug() << "Got Boundary from kio-http '" << tmp << "'" << endl; 00179 if ( !tmp.isEmpty() ) { 00180 m_boundary = QCString("--")+tmp.latin1(); 00181 m_boundaryLength = m_boundary.length(); 00182 } 00183 } 00184 // Append to m_currentLine until eol 00185 for ( uint i = 0; i < data.size() ; ++i ) 00186 { 00187 // Store char. Skip if '\n' and currently parsing a header. 00188 m_lineParser->addChar( data[i], !m_bParsingHeader ); 00189 if ( m_lineParser->isLineComplete() ) 00190 { 00191 QByteArray lineData = m_lineParser->currentLine(); 00192 #ifdef DEBUG_PARSING 00193 kdDebug() << "lineData.size()=" << lineData.size() << endl; 00194 #endif 00195 QCString line( lineData.data(), lineData.size()+1 ); // deep copy 00196 // 0-terminate the data, but only for the line-based tests below 00197 // We want to keep the raw data in case it ends up in sendData() 00198 int sz = line.size(); 00199 if ( sz > 0 ) 00200 line[sz-1] = '\0'; 00201 #ifdef DEBUG_PARSING 00202 kdDebug() << "[" << m_bParsingHeader << "] line='" << line << "'" << endl; 00203 #endif 00204 if ( m_bParsingHeader ) 00205 { 00206 if ( !line.isEmpty() ) 00207 m_bGotAnyHeader = true; 00208 if ( m_boundary.isNull() ) 00209 { 00210 if ( !line.isEmpty() ) { 00211 #ifdef DEBUG_PARSING 00212 kdDebug() << "Boundary is " << line << endl; 00213 #endif 00214 m_boundary = line; 00215 m_boundaryLength = m_boundary.length(); 00216 } 00217 } 00218 else if ( !qstrnicmp( line.data(), "Content-Encoding:", 17 ) ) 00219 { 00220 QString encoding = QString::fromLatin1(line.data()+17).stripWhiteSpace().lower(); 00221 if (encoding == "gzip" || encoding == "x-gzip") { 00222 m_gzip = true; 00223 } else { 00224 kdDebug() << "FIXME: unhandled encoding type in KMultiPart: " << encoding << endl; 00225 } 00226 } 00227 // parse Content-Type 00228 else if ( !qstrnicmp( line.data(), "Content-Type:", 13 ) ) 00229 { 00230 Q_ASSERT( m_nextMimeType.isNull() ); 00231 m_nextMimeType = QString::fromLatin1( line.data() + 14 ).stripWhiteSpace(); 00232 kdDebug() << "m_nextMimeType=" << m_nextMimeType << endl; 00233 } 00234 // Empty line, end of headers (if we had any header line before) 00235 else if ( line.isEmpty() && m_bGotAnyHeader ) 00236 { 00237 m_bParsingHeader = false; 00238 #ifdef DEBUG_PARSING 00239 kdDebug() << "end of headers" << endl; 00240 #endif 00241 startOfData(); 00242 } 00243 // First header (when we know it from kio_http) 00244 else if ( line == m_boundary ) 00245 ; // nothing to do 00246 else if ( !line.isEmpty() ) // this happens with e.g. Set-Cookie: 00247 kdDebug() << "Ignoring header " << line << endl; 00248 } else { 00249 if ( !qstrncmp( line, m_boundary, m_boundaryLength ) ) 00250 { 00251 #ifdef DEBUG_PARSING 00252 kdDebug() << "boundary found!" << endl; 00253 kdDebug() << "after it is " << line.data() + m_boundaryLength << endl; 00254 #endif 00255 // Was it the very last boundary ? 00256 if ( !qstrncmp( line.data() + m_boundaryLength, "--", 2 ) ) 00257 { 00258 #ifdef DEBUG_PARSING 00259 kdDebug() << "Completed!" << endl; 00260 #endif 00261 endOfData(); 00262 emit completed(); 00263 } else 00264 { 00265 char nextChar = *(line.data() + m_boundaryLength); 00266 #ifdef DEBUG_PARSING 00267 kdDebug() << "KMultiPart::slotData nextChar='" << nextChar << "'" << endl; 00268 #endif 00269 if ( nextChar == '\n' || nextChar == '\r' ) { 00270 endOfData(); 00271 startHeader(); 00272 } 00273 else { 00274 // otherwise, false hit, it has trailing stuff 00275 sendData( lineData ); 00276 } 00277 } 00278 } else { 00279 // send to part 00280 sendData( lineData ); 00281 } 00282 } 00283 m_lineParser->clearLine(); 00284 } 00285 } 00286 } 00287 00288 void KMultiPart::setPart( const QString& mimeType ) 00289 { 00290 KXMLGUIFactory *guiFactory = factory(); 00291 if ( guiFactory ) // seems to be 0 when restoring from SM 00292 guiFactory->removeClient( this ); 00293 kdDebug() << "KMultiPart::setPart " << mimeType << endl; 00294 delete m_part; 00295 // Try to find an appropriate viewer component 00296 m_part = KParts::ComponentFactory::createPartInstanceFromQuery<KParts::ReadOnlyPart> 00297 ( m_mimeType, QString::null, widget(), 0L, this, 0L ); 00298 if ( !m_part ) { 00299 // TODO launch external app 00300 KMessageBox::error( widget(), i18n("No handler found for %1!").arg(m_mimeType) ); 00301 return; 00302 } 00303 // By making the part a child XMLGUIClient of ours, we get its GUI merged in. 00304 insertChildClient( m_part ); 00305 m_part->widget()->show(); 00306 00307 connect( m_part, SIGNAL( completed() ), 00308 this, SLOT( slotPartCompleted() ) ); 00309 00310 KParts::BrowserExtension* childExtension = KParts::BrowserExtension::childObject( m_part ); 00311 00312 if ( childExtension ) 00313 { 00314 00315 // Forward signals from the part's browser extension 00316 // this is very related (but not exactly like) KHTMLPart::processObjectRequest 00317 00318 connect( childExtension, SIGNAL( openURLNotify() ), 00319 m_extension, SIGNAL( openURLNotify() ) ); 00320 00321 connect( childExtension, SIGNAL( openURLRequestDelayed( const KURL &, const KParts::URLArgs & ) ), 00322 m_extension, SIGNAL( openURLRequest( const KURL &, const KParts::URLArgs & ) ) ); 00323 00324 connect( childExtension, SIGNAL( createNewWindow( const KURL &, const KParts::URLArgs & ) ), 00325 m_extension, SIGNAL( createNewWindow( const KURL &, const KParts::URLArgs & ) ) ); 00326 connect( childExtension, SIGNAL( createNewWindow( const KURL &, const KParts::URLArgs &, const KParts::WindowArgs &, KParts::ReadOnlyPart *& ) ), 00327 m_extension, SIGNAL( createNewWindow( const KURL &, const KParts::URLArgs & , const KParts::WindowArgs &, KParts::ReadOnlyPart *&) ) ); 00328 00329 // Keep in sync with khtml_part.cpp 00330 connect( childExtension, SIGNAL( popupMenu( const QPoint &, const KFileItemList & ) ), 00331 m_extension, SIGNAL( popupMenu( const QPoint &, const KFileItemList & ) ) ); 00332 connect( childExtension, SIGNAL( popupMenu( KXMLGUIClient *, const QPoint &, const KFileItemList & ) ), 00333 m_extension, SIGNAL( popupMenu( KXMLGUIClient *, const QPoint &, const KFileItemList & ) ) ); 00334 connect( childExtension, SIGNAL( popupMenu( KXMLGUIClient *, const QPoint &, const KFileItemList &, const KParts::URLArgs &, KParts::BrowserExtension::PopupFlags ) ), 00335 m_extension, SIGNAL( popupMenu( KXMLGUIClient *, const QPoint &, const KFileItemList &, const KParts::URLArgs &, KParts::BrowserExtension::PopupFlags ) ) ); 00336 connect( childExtension, SIGNAL( popupMenu( const QPoint &, const KURL &, const QString &, mode_t ) ), 00337 m_extension, SIGNAL( popupMenu( const QPoint &, const KURL &, const QString &, mode_t ) ) ); 00338 connect( childExtension, SIGNAL( popupMenu( KXMLGUIClient *, const QPoint &, const KURL &, const QString &, mode_t ) ), 00339 m_extension, SIGNAL( popupMenu( KXMLGUIClient *, const QPoint &, const KURL &, const QString &, mode_t ) ) ); 00340 connect( childExtension, SIGNAL( popupMenu( KXMLGUIClient *, const QPoint &, const KURL &, const KParts::URLArgs &, KParts::BrowserExtension::PopupFlags, mode_t ) ), 00341 m_extension, SIGNAL( popupMenu( KXMLGUIClient *, const QPoint &, const KURL &, const KParts::URLArgs &, KParts::BrowserExtension::PopupFlags, mode_t ) ) ); 00342 00343 00344 connect( childExtension, SIGNAL( infoMessage( const QString & ) ), 00345 m_extension, SIGNAL( infoMessage( const QString & ) ) ); 00346 00347 childExtension->setBrowserInterface( m_extension->browserInterface() ); 00348 00349 connect( childExtension, SIGNAL( enableAction( const char *, bool ) ), 00350 m_extension, SIGNAL( enableAction( const char *, bool ) ) ); 00351 connect( childExtension, SIGNAL( setLocationBarURL( const QString& ) ), 00352 m_extension, SIGNAL( setLocationBarURL( const QString& ) ) ); 00353 connect( childExtension, SIGNAL( setIconURL( const KURL& ) ), 00354 m_extension, SIGNAL( setIconURL( const KURL& ) ) ); 00355 connect( childExtension, SIGNAL( loadingProgress( int ) ), 00356 m_extension, SIGNAL( loadingProgress( int ) ) ); 00357 connect( childExtension, SIGNAL( speedProgress( int ) ), 00358 m_extension, SIGNAL( speedProgress( int ) ) ); 00359 connect( childExtension, SIGNAL( selectionInfo( const KFileItemList& ) ), 00360 m_extension, SIGNAL( selectionInfo( const KFileItemList& ) ) ); 00361 connect( childExtension, SIGNAL( selectionInfo( const QString& ) ), 00362 m_extension, SIGNAL( selectionInfo( const QString& ) ) ); 00363 connect( childExtension, SIGNAL( selectionInfo( const KURL::List& ) ), 00364 m_extension, SIGNAL( selectionInfo( const KURL::List& ) ) ); 00365 connect( childExtension, SIGNAL( mouseOverInfo( const KFileItem* ) ), 00366 m_extension, SIGNAL( mouseOverInfo( const KFileItem* ) ) ); 00367 connect( childExtension, SIGNAL( moveTopLevelWidget( int, int ) ), 00368 m_extension, SIGNAL( moveTopLevelWidget( int, int ) ) ); 00369 connect( childExtension, SIGNAL( resizeTopLevelWidget( int, int ) ), 00370 m_extension, SIGNAL( resizeTopLevelWidget( int, int ) ) ); 00371 } 00372 00373 m_isHTMLPart = ( mimeType == "text/html" ); 00374 m_partIsLoading = false; 00375 // Load the part's plugins too. 00376 // ###### This is a hack. The bug is that KHTMLPart doesn't load its plugins 00377 // if className != "Browser/View". 00378 loadPlugins( this, m_part, m_part->instance() ); 00379 // Get the part's GUI to appear 00380 if ( guiFactory ) 00381 guiFactory->addClient( this ); 00382 } 00383 00384 void KMultiPart::startOfData() 00385 { 00386 kdDebug() << "KMultiPart::startOfData" << endl; 00387 Q_ASSERT( !m_nextMimeType.isNull() ); 00388 if( m_nextMimeType.isNull() ) 00389 return; 00390 00391 if ( m_gzip ) 00392 { 00393 m_filter = new HTTPFilterGZip; 00394 connect( m_filter, SIGNAL( output( const QByteArray& ) ), this, SLOT( reallySendData( const QByteArray& ) ) ); 00395 } 00396 00397 if ( m_mimeType != m_nextMimeType ) 00398 { 00399 // Need to switch parts (or create the initial one) 00400 m_mimeType = m_nextMimeType; 00401 setPart( m_mimeType ); 00402 } 00403 Q_ASSERT( m_part ); 00404 // Pass URLArgs (e.g. reload) 00405 KParts::BrowserExtension* childExtension = KParts::BrowserExtension::childObject( m_part ); 00406 if ( childExtension ) 00407 childExtension->setURLArgs( m_extension->urlArgs() ); 00408 00409 m_nextMimeType = QString::null; 00410 delete m_tempFile; 00411 m_tempFile = 0L; 00412 if ( m_isHTMLPart ) 00413 { 00414 KHTMLPart* htmlPart = static_cast<KHTMLPart *>( static_cast<KParts::ReadOnlyPart *>( m_part ) ); 00415 htmlPart->begin( url() ); 00416 } 00417 else 00418 { 00419 // ###### TODO use a QByteArray and a data: URL instead 00420 m_tempFile = new KTempFile; 00421 } 00422 } 00423 00424 void KMultiPart::sendData( const QByteArray& line ) 00425 { 00426 if ( m_filter ) 00427 { 00428 m_filter->slotInput( line ); 00429 } 00430 else 00431 { 00432 reallySendData( line ); 00433 } 00434 } 00435 00436 void KMultiPart::reallySendData( const QByteArray& line ) 00437 { 00438 if ( m_isHTMLPart ) 00439 { 00440 KHTMLPart* htmlPart = static_cast<KHTMLPart *>( static_cast<KParts::ReadOnlyPart *>( m_part ) ); 00441 htmlPart->write( line.data(), line.size() ); 00442 } 00443 else if ( m_tempFile ) 00444 { 00445 m_tempFile->file()->writeBlock( line.data(), line.size() ); 00446 } 00447 } 00448 00449 void KMultiPart::endOfData() 00450 { 00451 Q_ASSERT( m_part ); 00452 if ( m_isHTMLPart ) 00453 { 00454 KHTMLPart* htmlPart = static_cast<KHTMLPart *>( static_cast<KParts::ReadOnlyPart *>( m_part ) ); 00455 htmlPart->end(); 00456 } else if ( m_tempFile ) 00457 { 00458 m_tempFile->close(); 00459 if ( m_partIsLoading ) 00460 { 00461 // The part is still loading the last data! Let it proceed then 00462 // Otherwise we'd keep cancelling it, and nothing would ever show up... 00463 kdDebug() << "KMultiPart::endOfData part isn't ready, skipping frame" << endl; 00464 m_tempFile->setAutoDelete( true ); 00465 } 00466 else 00467 { 00468 kdDebug() << "KMultiPart::endOfData opening " << m_tempFile->name() << endl; 00469 KURL url; 00470 url.setPath( m_tempFile->name() ); 00471 m_partIsLoading = true; 00472 (void) m_part->openURL( url ); 00473 } 00474 delete m_tempFile; 00475 m_tempFile = 0L; 00476 } 00477 } 00478 00479 void KMultiPart::slotPartCompleted() 00480 { 00481 if ( !m_isHTMLPart ) 00482 { 00483 Q_ASSERT( m_part ); 00484 // Delete temp file used by the part 00485 Q_ASSERT( m_part->url().isLocalFile() ); 00486 kdDebug() << "slotPartCompleted deleting " << m_part->url().path() << endl; 00487 (void) unlink( QFile::encodeName( m_part->url().path() ) ); 00488 m_partIsLoading = false; 00489 // Do not emit completed from here. 00490 } 00491 } 00492 00493 bool KMultiPart::closeURL() 00494 { 00495 if ( m_part ) 00496 return m_part->closeURL(); 00497 return true; 00498 } 00499 00500 void KMultiPart::guiActivateEvent( KParts::GUIActivateEvent * ) 00501 { 00502 // Not public! 00503 //if ( m_part ) 00504 // m_part->guiActivateEvent( e ); 00505 } 00506 00507 void KMultiPart::slotJobFinished( KIO::Job *job ) 00508 { 00509 if ( job->error() ) 00510 { 00511 // TODO use khtml's error:// scheme 00512 job->showErrorDialog(); 00513 emit canceled( job->errorString() ); 00514 } 00515 else 00516 { 00517 /*if ( m_khtml->view()->contentsY() == 0 ) 00518 { 00519 KParts::URLArgs args = m_ext->urlArgs(); 00520 m_khtml->view()->setContentsPos( args.xOffset, args.yOffset ); 00521 }*/ 00522 00523 emit completed(); 00524 00525 //QTimer::singleShot( 0, this, SLOT( updateWindowCaption() ) ); 00526 } 00527 m_job = 0L; 00528 } 00529 00530 KAboutData* KMultiPart::createAboutData() 00531 { 00532 KAboutData* aboutData = new KAboutData( "kmultipart", I18N_NOOP("KMultiPart"), 00533 "0.1", 00534 I18N_NOOP( "Embeddable component for multipart/mixed" ), 00535 KAboutData::License_GPL, 00536 "(c) 2001, David Faure <david@mandrakesoft.com>"); 00537 return aboutData; 00538 } 00539 00540 #if 0 00541 KMultiPartBrowserExtension::KMultiPartBrowserExtension( KMultiPart *parent, const char *name ) 00542 : KParts::BrowserExtension( parent, name ) 00543 { 00544 m_imgPart = parent; 00545 } 00546 00547 int KMultiPartBrowserExtension::xOffset() 00548 { 00549 return m_imgPart->doc()->view()->contentsX(); 00550 } 00551 00552 int KMultiPartBrowserExtension::yOffset() 00553 { 00554 return m_imgPart->doc()->view()->contentsY(); 00555 } 00556 00557 void KMultiPartBrowserExtension::print() 00558 { 00559 static_cast<KHTMLPartBrowserExtension *>( m_imgPart->doc()->browserExtension() )->print(); 00560 } 00561 00562 void KMultiPartBrowserExtension::reparseConfiguration() 00563 { 00564 static_cast<KHTMLPartBrowserExtension *>( m_imgPart->doc()->browserExtension() )->reparseConfiguration(); 00565 m_imgPart->doc()->setAutoloadImages( true ); 00566 } 00567 #endif 00568 00569 #include "kmultipart.moc"
KDE Logo
This file is part of the documentation for khtml Library Version 3.2.3.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Mar 16 17:23:48 2005 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003