• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

KDEWebKit

kwebpage.cpp

Go to the documentation of this file.
00001 /*
00002  * This file is part of the KDE project.
00003  *
00004  * Copyright (C) 2008 Dirk Mueller <mueller@kde.org>
00005  * Copyright (C) 2008 Urs Wolfer <uwolfer @ kde.org>
00006  * Copyright (C) 2008 Michael Howell <mhowell123@gmail.com>
00007  * Copyright (C) 2009,2010 Dawit Alemayehu <adawit@kde.org>
00008  *
00009  * This library is free software; you can redistribute it and/or
00010  * modify it under the terms of the GNU Library General Public
00011  * License as published by the Free Software Foundation; either
00012  * version 2 of the License, or (at your option) any later version.
00013  *
00014  * This library is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017  * Library General Public License for more details.
00018  *
00019  * You should have received a copy of the GNU Library General Public License
00020  * along with this library; see the file COPYING.LIB.  If not, write to
00021  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00022  * Boston, MA 02110-1301, USA.
00023  *
00024  */
00025 
00026 // Own
00027 #include "kwebpage.h"
00028 #include "kwebwallet.h"
00029 
00030 // Local
00031 #include "kwebpluginfactory.h"
00032 
00033 // KDE
00034 #include <kaction.h>
00035 #include <kfiledialog.h>
00036 #include <kprotocolmanager.h>
00037 #include <kjobuidelegate.h>
00038 #include <krun.h>
00039 #include <kstandarddirs.h>
00040 #include <kstandardshortcut.h>
00041 #include <kurl.h>
00042 #include <kdebug.h>
00043 #include <kmimetypetrader.h>
00044 #include <klocalizedstring.h>
00045 #include <ktemporaryfile.h>
00046 #include <kio/accessmanager.h>
00047 #include <kio/job.h>
00048 #include <kio/jobuidelegate.h>
00049 #include <kio/renamedialog.h>
00050 #include <kio/scheduler.h>
00051 #include <kparts/browseropenorsavequestion.h>
00052 
00053 // Qt
00054 #include <QtCore/QPointer>
00055 #include <QtCore/QFileInfo>
00056 #include <QtCore/QCoreApplication>
00057 #include <QtWebKit/QWebFrame>
00058 #include <QtNetwork/QNetworkReply>
00059 
00060 
00061 #define QL1S(x)  QLatin1String(x)
00062 #define QL1C(x)  QLatin1Char(x)
00063 
00064 static bool isMimeTypeAssociatedWithSelf(const QString& mimeType, const KService::Ptr &offer)
00065 {
00066     Q_UNUSED(mimeType);
00067     
00068     if (!offer)
00069         return false;
00070     
00071     kDebug(800) << offer->desktopEntryName();
00072 
00073     const QString& appName = QCoreApplication::applicationName();
00074 
00075     if (appName == offer->desktopEntryName() || offer->exec().trimmed().startsWith(appName))
00076         return true;
00077 
00078     // konqueror exception since it uses kfmclient to open html content...
00079     if (appName == QL1S("konqueror") && offer->exec().trimmed().startsWith(QL1S("kfmclient")))
00080         return true;
00081 
00082     return false;
00083 }
00084 
00085 static void extractMimeType(const QNetworkReply* reply, QString& mimeType)
00086 {
00087     mimeType.clear();
00088     const KIO::MetaData& metaData = reply->attribute(static_cast<QNetworkRequest::Attribute>(KIO::AccessManager::MetaData)).toMap();
00089     if (metaData.contains(QL1S("content-type")))
00090         mimeType = metaData.value(QL1S("content-type"));
00091 
00092     if (!mimeType.isEmpty())
00093         return;
00094 
00095     if (!reply->hasRawHeader("Content-Type"))
00096         return;
00097 
00098     const QString value (QL1S(reply->rawHeader("Content-Type").simplified().constData()));
00099     const int index = value.indexOf(QL1C(';'));
00100     mimeType = ((index == -1) ? value : value.left(index));
00101 }
00102 
00103 static KUrl promptUser (QWidget *parent, const KUrl& url, const QString& suggestedName)
00104 {
00105     KUrl destUrl;
00106     int result = KIO::R_OVERWRITE;
00107     const QString fileName ((suggestedName.isEmpty() ? url.fileName() : suggestedName));
00108 
00109     do {
00110         // convert filename to URL using fromPath to avoid trouble with ':' in filenames (#184202)
00111         destUrl = KFileDialog::getSaveFileName(KUrl::fromPath(fileName), QString(), parent);
00112 
00113         if (destUrl.isLocalFile()) {
00114             QFileInfo finfo (destUrl.toLocalFile());
00115             if (finfo.exists()) {
00116                 QDateTime now = QDateTime::currentDateTime();
00117                 KIO::RenameDialog dlg (parent, i18n("Overwrite File?"), url, destUrl,
00118                                        KIO::RenameDialog_Mode(KIO::M_OVERWRITE | KIO::M_SKIP),
00119                                        -1, finfo.size(),
00120                                        now.toTime_t(), finfo.created().toTime_t(),
00121                                        now.toTime_t(), finfo.lastModified().toTime_t());
00122                 result = dlg.exec();
00123             }
00124         }
00125     } while (result == KIO::R_CANCEL && destUrl.isValid());
00126 
00127     return destUrl;
00128 }
00129 
00130 static bool downloadResource (const KUrl& srcUrl, const QString& suggestedName = QString(),
00131                               QWidget* parent = 0, const KIO::MetaData& metaData = KIO::MetaData())
00132 {
00133     const KUrl& destUrl = promptUser(parent, srcUrl, suggestedName);
00134 
00135     if (!destUrl.isValid())
00136         return false;
00137     
00138     KIO::Job *job = KIO::file_copy(srcUrl, destUrl, -1, KIO::Overwrite);
00139     
00140     if (!metaData.isEmpty())
00141         job->setMetaData(metaData);
00142 
00143     job->addMetaData(QL1S("MaxCacheSize"), QL1S("0")); // Don't store in http cache.
00144     job->addMetaData(QL1S("cache"), QL1S("cache")); // Use entry from cache if available.
00145     job->uiDelegate()->setAutoErrorHandlingEnabled(true);
00146     return true;
00147 }
00148 
00149 static bool isReplyStatusOk(const QNetworkReply* reply)
00150 {
00151     if (!reply || reply->error() != QNetworkReply::NoError)
00152         return false;
00153 
00154     // Check HTTP status code only for http and webdav protocols...
00155     const QString scheme = reply->url().scheme();
00156     if (scheme.startsWith(QLatin1String("http"), Qt::CaseInsensitive) ||
00157         scheme.startsWith(QLatin1String("webdav"), Qt::CaseInsensitive)) {
00158         bool ok = false;
00159         const int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(&ok);
00160         if (!ok || statusCode < 200 || statusCode > 299)
00161             return false;
00162     }
00163 
00164     return true;
00165 }
00166 
00167 class KWebPage::KWebPagePrivate
00168 {
00169 public:
00170     KWebPagePrivate() : inPrivateBrowsingMode(false) {}
00171     void _k_copyResultToTempFile(KJob * job)
00172     {
00173         if ( job->error() )
00174             job->uiDelegate()->showErrorMessage();
00175         else // Same as KRun::foundMimeType but with a different URL
00176             (void)KRun::runUrl(static_cast<KIO::FileCopyJob *>(job)->destUrl(), mimeType, window);
00177     }
00178 
00179     QPointer<QWidget> window;
00180     QString mimeType;
00181     QPointer<KWebWallet> wallet;
00182     bool inPrivateBrowsingMode;
00183 };
00184 
00185 
00186 KWebPage::KWebPage(QObject *parent, Integration flags)
00187          :QWebPage(parent), d(new KWebPagePrivate)
00188 { 
00189     // KDE KParts integration for <embed> tag...
00190     if (!flags || (flags & KPartsIntegration))
00191         setPluginFactory(new KWebPluginFactory(this));
00192 
00193     WId windowId = 0;
00194     QWidget *widget = qobject_cast<QWidget*>(parent);
00195     if (widget && widget->window())
00196         windowId = widget->window()->winId();
00197 
00198     // KDE IO (KIO) integration...
00199     if (!flags || (flags & KIOIntegration)) {
00200         KIO::Integration::AccessManager *manager = new KIO::Integration::AccessManager(this);
00201         // Disable QtWebKit's internal cache to avoid duplication with the one in KIO...
00202         manager->setCache(0);
00203         manager->setCookieJarWindowId(windowId);
00204         setNetworkAccessManager(manager);
00205     }
00206 
00207     // KWallet integration...
00208     if (!flags || (flags & KWalletIntegration))
00209         setWallet(new KWebWallet(0, windowId));
00210 
00211     action(Back)->setIcon(KIcon("go-previous"));
00212     action(Forward)->setIcon(KIcon("go-next"));
00213     action(Reload)->setIcon(KIcon("view-refresh"));
00214     action(Stop)->setIcon(KIcon("process-stop"));
00215     action(Cut)->setIcon(KIcon("edit-cut"));
00216     action(Copy)->setIcon(KIcon("edit-copy"));
00217     action(Paste)->setIcon(KIcon("edit-paste"));
00218     action(Undo)->setIcon(KIcon("edit-undo"));
00219     action(Redo)->setIcon(KIcon("edit-redo"));
00220     action(InspectElement)->setIcon(KIcon("view-process-all"));
00221     action(OpenLinkInNewWindow)->setIcon(KIcon("window-new"));
00222     action(OpenFrameInNewWindow)->setIcon(KIcon("window-new"));
00223     action(OpenImageInNewWindow)->setIcon(KIcon("window-new"));
00224     action(CopyLinkToClipboard)->setIcon(KIcon("edit-copy"));
00225     action(CopyImageToClipboard)->setIcon(KIcon("edit-copy"));
00226     action(ToggleBold)->setIcon(KIcon("format-text-bold"));
00227     action(ToggleItalic)->setIcon(KIcon("format-text-italic"));
00228     action(ToggleUnderline)->setIcon(KIcon("format-text-underline"));
00229     action(DownloadLinkToDisk)->setIcon(KIcon("document-save"));
00230     action(DownloadImageToDisk)->setIcon(KIcon("document-save"));
00231 
00232     settings()->setWebGraphic(QWebSettings::MissingPluginGraphic, KIcon("preferences-plugin").pixmap(32, 32));
00233     settings()->setWebGraphic(QWebSettings::MissingImageGraphic, KIcon("image-missing").pixmap(32, 32));
00234     settings()->setWebGraphic(QWebSettings::DefaultFrameIconGraphic, KIcon("applications-internet").pixmap(32, 32));
00235 
00236     action(Back)->setShortcut(KStandardShortcut::back().primary());
00237     action(Forward)->setShortcut(KStandardShortcut::forward().primary());
00238     action(Reload)->setShortcut(KStandardShortcut::reload().primary());
00239     action(Stop)->setShortcut(QKeySequence(Qt::Key_Escape));
00240     action(Cut)->setShortcut(KStandardShortcut::cut().primary());
00241     action(Copy)->setShortcut(KStandardShortcut::copy().primary());
00242     action(Paste)->setShortcut(KStandardShortcut::paste().primary());
00243     action(Undo)->setShortcut(KStandardShortcut::undo().primary());
00244     action(Redo)->setShortcut(KStandardShortcut::redo().primary());
00245     action(SelectAll)->setShortcut(KStandardShortcut::selectAll().primary());
00246 }
00247 
00248 KWebPage::~KWebPage()
00249 {
00250     delete d;
00251 }
00252 
00253 bool KWebPage::isExternalContentAllowed() const
00254 {
00255     KIO::AccessManager *manager = qobject_cast<KIO::AccessManager*>(networkAccessManager());
00256     if (manager)
00257         return manager->isExternalContentAllowed();
00258     return true;
00259 }
00260 
00261 KWebWallet *KWebPage::wallet() const
00262 {
00263     return d->wallet;
00264 }
00265 
00266 void KWebPage::setAllowExternalContent(bool allow)
00267 {
00268     KIO::AccessManager *manager = qobject_cast<KIO::AccessManager*>(networkAccessManager());
00269     if (manager)
00270       manager->setExternalContentAllowed(allow);
00271 }
00272 
00273 void KWebPage::setWallet(KWebWallet* wallet)
00274 {
00275     // Delete the current wallet if this object is its parent...
00276     if (d->wallet && this == d->wallet->parent())
00277         delete d->wallet;
00278 
00279     d->wallet = wallet;
00280 
00281     if (d->wallet)
00282         d->wallet->setParent(this);
00283 }
00284 
00285 void KWebPage::downloadRequest(const QNetworkRequest &request)
00286 {
00287     downloadResource(request.url(), QString(), view(),
00288                      request.attribute(static_cast<QNetworkRequest::Attribute>(KIO::AccessManager::MetaData)).toMap());
00289 }
00290 
00291 void KWebPage::downloadUrl(const KUrl &url)
00292 {
00293     downloadResource(url, QString(), view());
00294 }
00295 
00296 void KWebPage::downloadResponse(QNetworkReply *reply)
00297 {
00298     Q_ASSERT(reply);
00299     
00300     if (!reply)
00301         return;
00302 
00303     // Put the job on hold only for the protocols we know about (read: http).
00304     KIO::Integration::AccessManager::putReplyOnHold(reply);
00305 
00306     // Reply url...
00307     const KUrl requestUrl (reply->request().url());
00308 
00309     // Get the top level window...
00310     QWidget* topLevelWindow = view() ? view()->window() : 0;
00311     
00312     // Get suggested file name...
00313     const KIO::MetaData& metaData = reply->attribute(static_cast<QNetworkRequest::Attribute>(KIO::AccessManager::MetaData)).toMap();
00314     const QString suggestedFileName = metaData.value(QL1S("content-disposition-filename"));
00315     const QString contentDispositionType = metaData.value(QL1S("content-disposition-type"));
00316    
00317     // Get the mime-type...
00318     QString mimeType;
00319     extractMimeType(reply, mimeType);
00320     // Convert executable text files to plain text...
00321     if (KParts::BrowserRun::isTextExecutable(mimeType))
00322         mimeType = QL1S("text/plain");
00323 
00324     //kDebug(800) << "Content-disposition:" << contentDispositionType << suggestedFileName;
00325     //kDebug(800) << "Got unsupported content of type:" << mimeType << "URL:" << requestUrl;
00326     //kDebug(800) << "Error code:" << reply->error() << reply->errorString();
00327 
00328     if (isReplyStatusOk(reply)) {
00329         KParts::BrowserOpenOrSaveQuestion::Result result;
00330         KParts::BrowserOpenOrSaveQuestion dlg(topLevelWindow, requestUrl, mimeType);
00331         dlg.setSuggestedFileName(suggestedFileName);
00332         dlg.setFeatures(KParts::BrowserOpenOrSaveQuestion::ServiceSelection);
00333         result = dlg.askOpenOrSave();
00334 
00335         switch (result) {
00336         case KParts::BrowserOpenOrSaveQuestion::Open:
00337             // Handle Post operations that return content...
00338             if (reply->operation() == QNetworkAccessManager::PostOperation) {
00339                 d->mimeType = mimeType;
00340                 d->window = topLevelWindow;
00341                 QFileInfo finfo (suggestedFileName.isEmpty() ? requestUrl.fileName() : suggestedFileName);
00342                 KTemporaryFile tempFile;
00343                 tempFile.setSuffix(QL1C('.') + finfo.suffix());
00344                 tempFile.setAutoRemove(false);
00345                 tempFile.open();
00346                 KUrl destUrl;
00347                 destUrl.setPath(tempFile.fileName());
00348                 KIO::Job *job = KIO::file_copy(requestUrl, destUrl, 0600, KIO::Overwrite);
00349                 job->ui()->setWindow(topLevelWindow);
00350                 connect(job, SIGNAL(result(KJob *)),
00351                         this, SLOT(_k_copyResultToTempFile(KJob*)));
00352                 return;
00353             }
00354 
00355             // Ask before running any executables...
00356             if (KParts::BrowserRun::allowExecution(mimeType, requestUrl)) {
00357                 KService::Ptr offer = dlg.selectedService();
00358                 // HACK: The check below is necessary to break an infinite
00359                 // recursion that occurs whenever this function is called as a result
00360                 // of receiving content that can be rendered by the app using this engine.
00361                 // For example a text/html header that containing a content-disposition
00362                 // header is received by the app using this class.
00363                 if (isMimeTypeAssociatedWithSelf(mimeType, offer)) {
00364                     QNetworkRequest req (reply->request());
00365                     req.setRawHeader("x-kdewebkit-ignore-disposition", "true");
00366                     currentFrame()->load(req);
00367                     return;
00368                 }
00369 
00370                 if (offer) {
00371                     KUrl::List list;
00372                     list.append(requestUrl);
00373                     //kDebug(800) << "Suggested file name:" << suggestedFileName;
00374                     if (offer->categories().contains(QL1S("KDE"), Qt::CaseInsensitive)) {
00375                         KIO::Scheduler::publishSlaveOnHold();
00376                         KRun::run(*offer, list, topLevelWindow , false, suggestedFileName);
00377                         return;
00378                     }
00379                     // For non KDE applications, we launch and kill the slave-on-hold...
00380                     KRun::run(*offer, list, topLevelWindow , false, suggestedFileName);
00381                 } else {
00382                     (void)new KRun(requestUrl, topLevelWindow);
00383                 }
00384             }
00385             break;
00386         case KParts::BrowserOpenOrSaveQuestion::Save:
00387             // Do not attempt to download directories and local files...
00388             if (mimeType == QL1S("inode/directory") || requestUrl.isLocalFile())
00389                 break;
00390 
00391             downloadResource(requestUrl, suggestedFileName, topLevelWindow);
00392             return;
00393         case KParts::BrowserOpenOrSaveQuestion::Cancel:
00394         default:
00395             break;
00396         }        
00397     } else {
00398         KService::Ptr offer = KMimeTypeTrader::self()->preferredService(mimeType);
00399         if (isMimeTypeAssociatedWithSelf(mimeType, offer)) {
00400             QNetworkRequest req (reply->request());
00401             req.setRawHeader("x-kdewebkit-ignore-disposition", "true");
00402             currentFrame()->load(req);
00403             return;
00404         }        
00405     }
00406     
00407     // Remove any ioslave that was put on hold...
00408     KIO::Scheduler::removeSlaveOnHold();
00409 }
00410 
00411 QString KWebPage::sessionMetaData(const QString &key) const
00412 {
00413     QString value;
00414 
00415     KIO::Integration::AccessManager *manager = qobject_cast<KIO::Integration::AccessManager *>(networkAccessManager());
00416     if (manager)
00417         value = manager->sessionMetaData().value(key);
00418 
00419     return value;
00420 }
00421 
00422 QString KWebPage::requestMetaData(const QString &key) const
00423 {
00424     QString value;
00425 
00426     KIO::Integration::AccessManager *manager = qobject_cast<KIO::Integration::AccessManager *>(networkAccessManager());
00427     if (manager)
00428         value = manager->requestMetaData().value(key);
00429 
00430     return value;
00431 }
00432 
00433 void KWebPage::setSessionMetaData(const QString &key, const QString &value)
00434 {
00435     KIO::Integration::AccessManager *manager = qobject_cast<KIO::Integration::AccessManager *>(networkAccessManager());
00436     if (manager)
00437         manager->sessionMetaData()[key] = value;
00438 }
00439 
00440 void KWebPage::setRequestMetaData(const QString &key, const QString &value)
00441 {
00442     KIO::Integration::AccessManager *manager = qobject_cast<KIO::Integration::AccessManager *>(networkAccessManager());
00443     if (manager)
00444         manager->requestMetaData()[key] = value;
00445 }
00446 
00447 void KWebPage::removeSessionMetaData(const QString &key)
00448 {
00449     KIO::Integration::AccessManager *manager = qobject_cast<KIO::Integration::AccessManager *>(networkAccessManager());
00450     if (manager)
00451         manager->sessionMetaData().remove(key);
00452 }
00453 
00454 void KWebPage::removeRequestMetaData(const QString &key)
00455 {
00456     KIO::Integration::AccessManager *manager = qobject_cast<KIO::Integration::AccessManager *>(networkAccessManager());
00457     if (manager)
00458         manager->requestMetaData().remove(key);
00459 }
00460 
00461 QString KWebPage::userAgentForUrl(const QUrl& _url) const
00462 {
00463     const KUrl url(_url);
00464     const QString userAgent = KProtocolManager::userAgentForHost((url.isLocalFile() ? QL1S("localhost") : url.host()));
00465 
00466     if (userAgent == KProtocolManager::defaultUserAgent())
00467         return QWebPage::userAgentForUrl(_url);
00468 
00469     return userAgent;
00470 }
00471 
00472 static void setDisableCookieJarStorage(QNetworkAccessManager* manager, bool status)
00473 {
00474     if (manager) {
00475         KIO::Integration::CookieJar *cookieJar = manager ? qobject_cast<KIO::Integration::CookieJar*>(manager->cookieJar()) : 0;
00476         if (cookieJar) {
00477             //kDebug(800) << "Store cookies ?" << !status;
00478             cookieJar->setDisableCookieStorage(status);
00479         }
00480     }
00481 }
00482 
00483 bool KWebPage::acceptNavigationRequest(QWebFrame *frame, const QNetworkRequest &request, NavigationType type)
00484 {
00485     kDebug(800) << "url:" << request.url() << ", type:" << type << ", frame:" << frame;
00486 
00487     if (frame && d->wallet && type == QWebPage::NavigationTypeFormSubmitted)
00488         d->wallet->saveFormData(frame);
00489 
00490     // Make sure nothing is cached when private browsing mode is enabled...
00491     if (settings()->testAttribute(QWebSettings::PrivateBrowsingEnabled)) {
00492         if (!d->inPrivateBrowsingMode) {
00493             setDisableCookieJarStorage(networkAccessManager(), true);
00494             setSessionMetaData(QL1S("no-cache"), QL1S("true"));
00495             d->inPrivateBrowsingMode = true;
00496         }
00497     } else  {
00498         if (d->inPrivateBrowsingMode) {
00499             setDisableCookieJarStorage(networkAccessManager(), false);
00500             removeSessionMetaData(QL1S("no-cache"));
00501             d->inPrivateBrowsingMode = false;
00502         }
00503     }
00504 
00505     /*
00506       If the navigation request is from the main frame, set the cross-domain
00507       meta-data value to the current url for proper integration with KCookieJar...
00508     */
00509     if (frame == mainFrame() && type != QWebPage::NavigationTypeReload)
00510         setSessionMetaData(QL1S("cross-domain"), request.url().toString());
00511 
00512     return QWebPage::acceptNavigationRequest(frame, request, type);
00513 }
00514 
00515 #include "kwebpage.moc"

KDEWebKit

Skip menu "KDEWebKit"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal