File indexing completed on 2024-04-21 14:57:39

0001 /* This file is part of the KDE project
0002    Copyright (C) 2002 David Faure <david@mandrakesoft.com>
0003 
0004    This library is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU Library General Public
0006    License as published by the Free Software Foundation; either
0007    version 2 of the License, or (at your option) any later version.
0008 
0009    This library is distributed in the hope that it will be useful,
0010    but WITHOUT ANY WARRANTY; without even the implied warranty of
0011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012    Library General Public License for more details.
0013 
0014    You should have received a copy of the GNU Library General Public License
0015    along with this library; see the file COPYING.LIB.  If not, write to
0016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017    Boston, MA 02110-1301, USA.
0018 */
0019 
0020 #include "kmultipart.h"
0021 
0022 #include "httpfiltergzip_p.h"
0023 #include <klocalizedstring.h>
0024 #include <kjobuidelegate.h>
0025 #include <kio/job.h>
0026 #include <QFile>
0027 #include <qtemporaryfile.h>
0028 #include <kmessagebox.h>
0029 #include <kmimetypetrader.h>
0030 #include <kpluginfactory.h>
0031 #include <khtml_part.h>
0032 #include <kxmlguifactory.h>
0033 #include <QTimer>
0034 #include <QVBoxLayout>
0035 #include <kaboutdata.h>
0036 
0037 static KAboutData kmultipartAboutData()
0038 {
0039     KAboutData aboutData("kmultipart", i18n("KMultiPart"),
0040                          "0.1",
0041                          i18n("Embeddable component for multipart/mixed"),
0042                          KAboutLicense::GPL,
0043                          i18n("Copyright 2001-2011, David Faure <faure@kde.org>"));
0044     return aboutData;
0045 }
0046 
0047 K_PLUGIN_FACTORY(KMultiPartFactory, registerPlugin<KMultiPart>();)
0048 
0049 //#define DEBUG_PARSING
0050 
0051 class KLineParser
0052 {
0053 public:
0054     KLineParser()
0055     {
0056         m_lineComplete = false;
0057     }
0058     void addChar(char c, bool storeNewline)
0059     {
0060         if (!storeNewline && c == '\r') {
0061             return;
0062         }
0063         Q_ASSERT(!m_lineComplete);
0064         if (storeNewline || c != '\n') {
0065             int sz = m_currentLine.size();
0066             m_currentLine.resize(sz + 1);
0067             m_currentLine[sz] = c;
0068         }
0069         if (c == '\n') {
0070             m_lineComplete = true;
0071         }
0072     }
0073     bool isLineComplete() const
0074     {
0075         return m_lineComplete;
0076     }
0077     QByteArray currentLine() const
0078     {
0079         return m_currentLine;
0080     }
0081     void clearLine()
0082     {
0083         Q_ASSERT(m_lineComplete);
0084         reset();
0085     }
0086     void reset()
0087     {
0088         m_currentLine.resize(0);
0089         m_lineComplete = false;
0090     }
0091 private:
0092     QByteArray m_currentLine;
0093     bool m_lineComplete; // true when ending with '\n'
0094 };
0095 
0096 /* testcase:
0097    Content-type: multipart/mixed;boundary=ThisRandomString
0098 
0099 --ThisRandomString
0100 Content-type: text/plain
0101 
0102 Data for the first object.
0103 
0104 --ThisRandomString
0105 Content-type: text/plain
0106 
0107 Data for the second and last object.
0108 
0109 --ThisRandomString--
0110 */
0111 
0112 KMultiPart::KMultiPart(QWidget *parentWidget,
0113                        QObject *parent, const QVariantList &)
0114     : KParts::ReadOnlyPart(parent)
0115 {
0116     m_filter = nullptr;
0117 
0118     setComponentData(kmultipartAboutData());
0119 
0120     QWidget *box = new QWidget(parentWidget);
0121     box->setLayout(new QVBoxLayout(box));
0122     setWidget(box);
0123 
0124     m_extension = new KParts::BrowserExtension(this);
0125 
0126     m_part = nullptr;
0127     m_isHTMLPart = false;
0128     m_job = nullptr;
0129     m_lineParser = new KLineParser;
0130     m_tempFile = nullptr;
0131 
0132     m_timer = new QTimer(this);
0133     connect(m_timer, SIGNAL(timeout()), this, SLOT(slotProgressInfo()));
0134 }
0135 
0136 KMultiPart::~KMultiPart()
0137 {
0138     // important: delete the nested part before the part or qobject destructor runs.
0139     // we now delete the nested part which deletes the part's widget which makes
0140     // _OUR_ m_widget 0 which in turn avoids our part destructor to delete the
0141     // widget ;-)
0142     // ### additional note: it _can_ be that the part has been deleted before:
0143     // when we're in a html frameset and the view dies first, then it will also
0144     // kill the htmlpart
0145     if (m_part) {
0146         delete static_cast<KParts::ReadOnlyPart *>(m_part);
0147     }
0148     delete m_job;
0149     delete m_lineParser;
0150     if (m_tempFile) {
0151         m_tempFile->setAutoRemove(true);
0152         delete m_tempFile;
0153     }
0154     delete m_filter;
0155     m_filter = nullptr;
0156 }
0157 
0158 void KMultiPart::startHeader()
0159 {
0160     m_bParsingHeader = true; // we expect a header to come first
0161     m_bGotAnyHeader = false;
0162     m_gzip = false;
0163     // just to be sure for now
0164     delete m_filter;
0165     m_filter = nullptr;
0166 }
0167 
0168 bool KMultiPart::openUrl(const QUrl &url)
0169 {
0170     setUrl(url);
0171     m_lineParser->reset();
0172     startHeader();
0173 
0174     //m_mimeType = arguments().mimeType();
0175 
0176     // Hmm, args.reload is set to true when reloading, but this doesn't seem to be enough...
0177     // I get "HOLD: Reusing held worker for <url>", and the old data
0178 
0179     m_job = KIO::get(url,
0180                      arguments().reload() ? KIO::Reload : KIO::NoReload,
0181                      KIO::HideProgressInfo);
0182 
0183     emit started(nullptr /*m_job*/);   // don't pass the job, it would interfere with our own infoMessage
0184 
0185     connect(m_job, SIGNAL(result(KJob*)),
0186             this, SLOT(slotJobFinished(KJob*)));
0187     connect(m_job, SIGNAL(data(KIO::Job*,QByteArray)),
0188             this, SLOT(slotData(KIO::Job*,QByteArray)));
0189 
0190     m_numberOfFrames = 0;
0191     m_numberOfFramesSkipped = 0;
0192     m_totalNumberOfFrames = 0;
0193     m_qtime.start();
0194     m_timer->start(1000);   //1s
0195 
0196     return true;
0197 }
0198 
0199 // Yes, libkdenetwork's has such a parser already (MultiPart),
0200 // but it works on the complete string, expecting the whole data to be available....
0201 // The version here is asynchronous.
0202 void KMultiPart::slotData(KIO::Job *job, const QByteArray &data)
0203 {
0204     if (m_boundary.isNull()) {
0205         QString tmp = job->queryMetaData("media-boundary");
0206         // qCDebug(KHTML_LOG) << "Got Boundary from kio-http '" << tmp << "'";
0207         if (!tmp.isEmpty()) {
0208             // as per r437578, sometimes we se something like this:
0209             // Content-Type: multipart/x-mixed-replace; boundary=--myboundary
0210             // ..
0211             // --myboundary
0212             // e.g. the hashes specified in the header are extra. However,
0213             // we also see the following on the w3c bugzilla:
0214             // boundary="------- =_aaaaaaaaaa0"
0215             // ..
0216             //--------- =_aaaaaaaaaa0
0217             // e.g. the hashes are accurate. For now, we consider the quoted
0218             // case to be quirk-free, and only apply the -- stripping quirk
0219             // when we're unquoted.
0220             if (tmp.startsWith(QLatin1String("--")) &&
0221                     job->queryMetaData("media-boundary-kio-quoted") != "true") {
0222                 m_boundary = tmp.toLatin1();
0223             } else {
0224                 m_boundary = QByteArray("--") + tmp.toLatin1();
0225             }
0226             m_boundaryLength = m_boundary.length();
0227         }
0228     }
0229     // Append to m_currentLine until eol
0230     for (int i = 0; i < data.size(); ++i) {
0231         // Store char. Skip if '\n' and currently parsing a header.
0232         m_lineParser->addChar(data[i], !m_bParsingHeader);
0233         if (m_lineParser->isLineComplete()) {
0234             QByteArray line = m_lineParser->currentLine();
0235 #ifdef DEBUG_PARSING
0236             // qCDebug(KHTML_LOG) << "line.size()=" << line.size();
0237 #endif
0238 #ifdef DEBUG_PARSING
0239             // qCDebug(KHTML_LOG) << "[" << m_bParsingHeader << "] line='" << line << "'";
0240 #endif
0241             if (m_bParsingHeader) {
0242                 if (!line.isEmpty()) {
0243                     m_bGotAnyHeader = true;
0244                 }
0245                 if (m_boundary.isNull()) {
0246                     if (!line.isEmpty()) {
0247 #ifdef DEBUG_PARSING
0248                         // qCDebug(KHTML_LOG) << "Boundary is " << line;
0249 #endif
0250                         m_boundary = line;
0251                         m_boundaryLength = m_boundary.length();
0252                     }
0253                 } else if (!qstrnicmp(line.data(), "Content-Encoding:", 17)) {
0254                     QString encoding = QString::fromLatin1(line.data() + 17).trimmed().toLower();
0255                     if (encoding == "gzip" || encoding == "x-gzip") {
0256                         m_gzip = true;
0257                     } else {
0258                         // qCDebug(KHTML_LOG) << "FIXME: unhandled encoding type in KMultiPart: " << encoding;
0259                     }
0260                 }
0261                 // parse Content-Type
0262                 else if (!qstrnicmp(line.data(), "Content-Type:", 13)) {
0263                     Q_ASSERT(m_nextMimeType.isNull());
0264                     m_nextMimeType = QString::fromLatin1(line.data() + 14).trimmed();
0265                     int semicolon = m_nextMimeType.indexOf(';');
0266                     if (semicolon != -1) {
0267                         m_nextMimeType = m_nextMimeType.left(semicolon);
0268                     }
0269                     // qCDebug(KHTML_LOG) << "m_nextMimeType=" << m_nextMimeType;
0270                 }
0271                 // Empty line, end of headers (if we had any header line before)
0272                 else if (line.isEmpty() && m_bGotAnyHeader) {
0273                     m_bParsingHeader = false;
0274 #ifdef DEBUG_PARSING
0275                     // qCDebug(KHTML_LOG) << "end of headers";
0276 #endif
0277                     startOfData();
0278                 }
0279                 // First header (when we know it from kio_http)
0280                 else if (line == m_boundary)
0281                     ; // nothing to do
0282                 else if (!line.isEmpty()) {   // this happens with e.g. Set-Cookie:
0283                     // qCDebug(KHTML_LOG) << "Ignoring header " << line;
0284                 }
0285             } else {
0286                 if (!qstrncmp(line, m_boundary, m_boundaryLength)) {
0287 #ifdef DEBUG_PARSING
0288                     // qCDebug(KHTML_LOG) << "boundary found!";
0289                     // qCDebug(KHTML_LOG) << "after it is " << line.data() + m_boundaryLength;
0290 #endif
0291                     // Was it the very last boundary ?
0292                     if (!qstrncmp(line.data() + m_boundaryLength, "--", 2)) {
0293 #ifdef DEBUG_PARSING
0294                         // qCDebug(KHTML_LOG) << "Completed!";
0295 #endif
0296                         endOfData();
0297                         emit completed();
0298                     } else {
0299                         char nextChar = *(line.data() + m_boundaryLength);
0300 #ifdef DEBUG_PARSING
0301                         // qCDebug(KHTML_LOG) << "KMultiPart::slotData nextChar='" << nextChar << "'";
0302 #endif
0303                         if (nextChar == '\n' || nextChar == '\r') {
0304                             endOfData();
0305                             startHeader();
0306                         } else {
0307                             // otherwise, false hit, it has trailing stuff
0308                             sendData(line);
0309                         }
0310                     }
0311                 } else {
0312                     // send to part
0313                     sendData(line);
0314                 }
0315             }
0316             m_lineParser->clearLine();
0317         }
0318     }
0319 }
0320 
0321 void KMultiPart::setPart(const QString &mimeType)
0322 {
0323     KXMLGUIFactory *guiFactory = factory();
0324     if (guiFactory) { // seems to be 0 when restoring from SM
0325         guiFactory->removeClient(this);
0326     }
0327     // qCDebug(KHTML_LOG) << "KMultiPart::setPart " << mimeType;
0328     delete m_part;
0329     // Try to find an appropriate viewer component
0330     m_part = KMimeTypeTrader::createPartInstanceFromQuery<KParts::ReadOnlyPart>
0331              (m_mimeType, widget(), this);
0332     widget()->layout()->addWidget(m_part->widget());
0333     if (!m_part) {
0334         // TODO launch external app
0335         KMessageBox::error(widget(), i18n("No handler found for %1.", m_mimeType));
0336         return;
0337     }
0338     // By making the part a child XMLGUIClient of ours, we get its GUI merged in.
0339     insertChildClient(m_part);
0340     m_part->widget()->show();
0341 
0342     connect(m_part, SIGNAL(completed()),
0343             this, SLOT(slotPartCompleted()));
0344     connect(m_part, SIGNAL(completed(bool)),
0345             this, SLOT(slotPartCompleted()));
0346 
0347     m_isHTMLPart = (mimeType == "text/html");
0348     KParts::BrowserExtension *childExtension = KParts::BrowserExtension::childObject(m_part);
0349 
0350     if (childExtension) {
0351 
0352         // Forward signals from the part's browser extension
0353         // this is very related (but not exactly like) KHTMLPart::processObjectRequest
0354 
0355         connect(childExtension, SIGNAL(openUrlNotify()),
0356                 m_extension, SIGNAL(openUrlNotify()));
0357 
0358         connect(childExtension, SIGNAL(openUrlRequestDelayed(QUrl,KParts::OpenUrlArguments,KParts::BrowserArguments)),
0359                 m_extension, SIGNAL(openUrlRequest(QUrl,KParts::OpenUrlArguments,KParts::BrowserArguments)));
0360 
0361         connect(childExtension, SIGNAL(createNewWindow(QUrl,KParts::OpenUrlArguments,KParts::BrowserArguments,KParts::WindowArgs,KParts::ReadOnlyPart**)),
0362                 m_extension, SIGNAL(createNewWindow(QUrl,KParts::OpenUrlArguments,KParts::BrowserArguments,KParts::WindowArgs,KParts::ReadOnlyPart**)));
0363 
0364         // Keep in sync with khtml_part.cpp
0365         connect(childExtension, SIGNAL(popupMenu(QPoint,KFileItemList,KParts::OpenUrlArguments,KParts::BrowserArguments,KParts::BrowserExtension::PopupFlags,KParts::BrowserExtension::ActionGroupMap)),
0366                 m_extension, SIGNAL(popupMenu(QPoint,KFileItemList,KParts::OpenUrlArguments,KParts::BrowserArguments,KParts::BrowserExtension::PopupFlags,KParts::BrowserExtension::ActionGroupMap)));
0367         connect(childExtension, SIGNAL(popupMenu(QPoint,QUrl,mode_t,KParts::OpenUrlArguments,KParts::BrowserArguments,KParts::BrowserExtension::PopupFlags,KParts::BrowserExtension::ActionGroupMap)),
0368                 m_extension, SIGNAL(popupMenu(QPoint,QUrl,mode_t,KParts::OpenUrlArguments,KParts::BrowserArguments,KParts::BrowserExtension::PopupFlags,KParts::BrowserExtension::ActionGroupMap)));
0369 
0370         if (m_isHTMLPart)
0371             connect(childExtension, SIGNAL(infoMessage(QString)),
0372                     m_extension, SIGNAL(infoMessage(QString)));
0373         // For non-HTML we prefer to show our infoMessage ourselves.
0374 
0375         childExtension->setBrowserInterface(m_extension->browserInterface());
0376 
0377         connect(childExtension, SIGNAL(enableAction(const char*,bool)),
0378                 m_extension, SIGNAL(enableAction(const char*,bool)));
0379         connect(childExtension, SIGNAL(setLocationBarUrl(QString)),
0380                 m_extension, SIGNAL(setLocationBarUrl(QString)));
0381         connect(childExtension, SIGNAL(setIconUrl(QUrl)),
0382                 m_extension, SIGNAL(setIconUrl(QUrl)));
0383         connect(childExtension, SIGNAL(loadingProgress(int)),
0384                 m_extension, SIGNAL(loadingProgress(int)));
0385         if (m_isHTMLPart)   // for non-HTML we have our own
0386             connect(childExtension, SIGNAL(speedProgress(int)),
0387                     m_extension, SIGNAL(speedProgress(int)));
0388         connect(childExtension, SIGNAL(selectionInfo(KFileItemList)),
0389                 m_extension, SIGNAL(selectionInfo(KFileItemList)));
0390         connect(childExtension, SIGNAL(selectionInfo(QString)),
0391                 m_extension, SIGNAL(selectionInfo(QString)));
0392         connect(childExtension, SIGNAL(selectionInfo(QList<QUrl>)),
0393                 m_extension, SIGNAL(selectionInfo(QList<QUrl>)));
0394         connect(childExtension, SIGNAL(mouseOverInfo(KFileItem)),
0395                 m_extension, SIGNAL(mouseOverInfo(KFileItem)));
0396         connect(childExtension, SIGNAL(moveTopLevelWidget(int,int)),
0397                 m_extension, SIGNAL(moveTopLevelWidget(int,int)));
0398         connect(childExtension, SIGNAL(resizeTopLevelWidget(int,int)),
0399                 m_extension, SIGNAL(resizeTopLevelWidget(int,int)));
0400     }
0401 
0402     m_partIsLoading = false;
0403     // Load the part's plugins too.
0404     // ###### This is a hack. The bug is that KHTMLPart doesn't load its plugins
0405     // if className != "Browser/View".
0406     loadPlugins(this, m_part, m_part->componentData());
0407     // Get the part's GUI to appear
0408     if (guiFactory) {
0409         guiFactory->addClient(this);
0410     }
0411 }
0412 
0413 void KMultiPart::startOfData()
0414 {
0415     // qCDebug(KHTML_LOG) << "KMultiPart::startOfData";
0416     Q_ASSERT(!m_nextMimeType.isNull());
0417     if (m_nextMimeType.isNull()) {
0418         return;
0419     }
0420 
0421     if (m_gzip) {
0422         // We can't use KFilterDev because it assumes it can read as much data as necessary
0423         // from the underlying device. It's a pull strategy, while KMultiPart has to do
0424         // a push strategy.
0425         m_filter = new HTTPFilterGZip;
0426         connect(m_filter, SIGNAL(output(QByteArray)), this, SLOT(reallySendData(QByteArray)));
0427     }
0428 
0429     if (m_mimeType != m_nextMimeType) {
0430         // Need to switch parts (or create the initial one)
0431         m_mimeType = m_nextMimeType;
0432         setPart(m_mimeType);
0433     }
0434     Q_ASSERT(m_part);
0435     // Pass args (e.g. reload)
0436     m_part->setArguments(arguments());
0437     KParts::BrowserExtension *childExtension = KParts::BrowserExtension::childObject(m_part);
0438     if (childExtension) {
0439         childExtension->setBrowserArguments(m_extension->browserArguments());
0440     }
0441 
0442     m_nextMimeType.clear();
0443     if (m_tempFile) {
0444         m_tempFile->setAutoRemove(true);
0445         delete m_tempFile;
0446         m_tempFile = nullptr;
0447     }
0448     if (m_isHTMLPart) {
0449         KHTMLPart *htmlPart = static_cast<KHTMLPart *>(static_cast<KParts::ReadOnlyPart *>(m_part));
0450         htmlPart->begin(url());
0451     } else {
0452         // ###### TODO use a QByteArray and a data: URL instead
0453         m_tempFile = new QTemporaryFile;
0454         m_tempFile->open();
0455     }
0456 }
0457 
0458 void KMultiPart::sendData(const QByteArray &line)
0459 {
0460     if (m_filter) {
0461         m_filter->slotInput(line);
0462     } else {
0463         reallySendData(line);
0464     }
0465 }
0466 
0467 void KMultiPart::reallySendData(const QByteArray &line)
0468 {
0469     if (m_isHTMLPart) {
0470         KHTMLPart *htmlPart = static_cast<KHTMLPart *>(static_cast<KParts::ReadOnlyPart *>(m_part));
0471         htmlPart->write(line.data(), line.size());
0472     } else if (m_tempFile) {
0473         m_tempFile->write(line.data(), line.size());
0474     }
0475 }
0476 
0477 void KMultiPart::endOfData()
0478 {
0479     Q_ASSERT(m_part);
0480     if (m_isHTMLPart) {
0481         KHTMLPart *htmlPart = static_cast<KHTMLPart *>(static_cast<KParts::ReadOnlyPart *>(m_part));
0482         htmlPart->end();
0483     } else if (m_tempFile) {
0484         const QString tempFileName = m_tempFile->fileName();
0485         m_tempFile->close();
0486         if (m_partIsLoading) {
0487             // The part is still loading the last data! Let it proceed then
0488             // Otherwise we'd keep canceling it, and nothing would ever show up...
0489             // qCDebug(KHTML_LOG) << "KMultiPart::endOfData part isn't ready, skipping frame";
0490             ++m_numberOfFramesSkipped;
0491             m_tempFile->setAutoRemove(true);
0492         } else {
0493             // qCDebug(KHTML_LOG) << "KMultiPart::endOfData opening " << tempFileName;
0494             QUrl url(tempFileName);
0495             m_partIsLoading = true;
0496             (void) m_part->openUrl(url);
0497         }
0498         delete m_tempFile;
0499         m_tempFile = nullptr;
0500     }
0501 }
0502 
0503 void KMultiPart::slotPartCompleted()
0504 {
0505     if (!m_isHTMLPart) {
0506         Q_ASSERT(m_part);
0507         // Delete temp file used by the part
0508         Q_ASSERT(m_part->url().isLocalFile());
0509         // qCDebug(KHTML_LOG) << "slotPartCompleted deleting " << m_part->url().toLocalFile();
0510         (void) unlink(QFile::encodeName(m_part->url().toLocalFile()));
0511         m_partIsLoading = false;
0512         ++m_numberOfFrames;
0513         // Do not emit completed from here.
0514     }
0515 }
0516 
0517 bool KMultiPart::closeUrl()
0518 {
0519     m_timer->stop();
0520     if (m_part) {
0521         return m_part->closeUrl();
0522     }
0523     return true;
0524 }
0525 
0526 void KMultiPart::guiActivateEvent(KParts::GUIActivateEvent *)
0527 {
0528     // Not public!
0529     //if ( m_part )
0530     //    m_part->guiActivateEvent( e );
0531 }
0532 
0533 void KMultiPart::slotJobFinished(KJob *job)
0534 {
0535     if (job->error()) {
0536         // TODO use khtml's error:// scheme
0537         job->uiDelegate()->showErrorMessage();
0538         emit canceled(job->errorString());
0539     } else {
0540         /*if ( m_khtml->view()->contentsY() == 0 )
0541         {
0542             const KParts::OpenUrlArguments args = arguments();
0543             m_khtml->view()->setContentsPos( args.xOffset(), args.yOffset() );
0544         }*/
0545 
0546         emit completed();
0547 
0548         //QTimer::singleShot( 0, this, SLOT(updateWindowCaption()) );
0549     }
0550     m_job = nullptr;
0551 }
0552 
0553 void KMultiPart::slotProgressInfo()
0554 {
0555     int time = m_qtime.elapsed();
0556     if (!time) {
0557         return;
0558     }
0559     if (m_totalNumberOfFrames == m_numberOfFrames + m_numberOfFramesSkipped) {
0560         return;    // No change, don't overwrite statusbar messages if any
0561     }
0562     //qCDebug(KHTML_LOG) << m_numberOfFrames << " in " << time << " milliseconds";
0563     QString str("%1 frames per second, %2 frames skipped per second");
0564     str = str.arg(1000.0 * (double)m_numberOfFrames / (double)time);
0565     str = str.arg(1000.0 * (double)m_numberOfFramesSkipped / (double)time);
0566     m_totalNumberOfFrames = m_numberOfFrames + m_numberOfFramesSkipped;
0567     //qCDebug(KHTML_LOG) << str;
0568     emit m_extension->infoMessage(str);
0569 }
0570 
0571 #if 0
0572 KMultiPartBrowserExtension::KMultiPartBrowserExtension(KMultiPart *parent, const char *name)
0573     : KParts::BrowserExtension(parent, name)
0574 {
0575     m_imgPart = parent;
0576 }
0577 
0578 int KMultiPartBrowserExtension::xOffset()
0579 {
0580     return m_imgPart->doc()->view()->contentsX();
0581 }
0582 
0583 int KMultiPartBrowserExtension::yOffset()
0584 {
0585     return m_imgPart->doc()->view()->contentsY();
0586 }
0587 
0588 void KMultiPartBrowserExtension::print()
0589 {
0590     static_cast<KHTMLPartBrowserExtension *>(m_imgPart->doc()->browserExtension())->print();
0591 }
0592 
0593 void KMultiPartBrowserExtension::reparseConfiguration()
0594 {
0595     static_cast<KHTMLPartBrowserExtension *>(m_imgPart->doc()->browserExtension())->reparseConfiguration();
0596     m_imgPart->doc()->setAutoloadImages(true);
0597 }
0598 #endif
0599 
0600 #include "kmultipart.moc"
0601 
0602 #include "moc_kmultipart.cpp"