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

KDEWebKit

  • kdewebkit
kwebpage.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the KDE project.
3  *
4  * Copyright (C) 2008 Dirk Mueller <mueller@kde.org>
5  * Copyright (C) 2008 Urs Wolfer <uwolfer @ kde.org>
6  * Copyright (C) 2008 Michael Howell <mhowell123@gmail.com>
7  * Copyright (C) 2009,2010 Dawit Alemayehu <adawit@kde.org>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public License
20  * along with this library; see the file COPYING.LIB. If not, write to
21  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  * Boston, MA 02110-1301, USA.
23  *
24  */
25 
26 // Own
27 #include "kwebpage.h"
28 #include "kwebwallet.h"
29 
30 // Local
31 #include "kwebpluginfactory.h"
32 
33 // KDE
34 #include <kaction.h>
35 #include <kfiledialog.h>
36 #include <kprotocolmanager.h>
37 #include <kjobuidelegate.h>
38 #include <krun.h>
39 #include <kstandarddirs.h>
40 #include <kstandardshortcut.h>
41 #include <kurl.h>
42 #include <kdebug.h>
43 #include <kshell.h>
44 #include <kmimetypetrader.h>
45 #include <klocalizedstring.h>
46 #include <ktemporaryfile.h>
47 #include <kio/accessmanager.h>
48 #include <kio/job.h>
49 #include <kio/copyjob.h>
50 #include <kio/jobuidelegate.h>
51 #include <kio/renamedialog.h>
52 #include <kparts/browseropenorsavequestion.h>
53 
54 // Qt
55 #include <QtCore/QPointer>
56 #include <QtCore/QFileInfo>
57 #include <QtCore/QCoreApplication>
58 #include <QtWebKit/QWebFrame>
59 #include <QtNetwork/QNetworkReply>
60 
61 
62 #define QL1S(x) QLatin1String(x)
63 #define QL1C(x) QLatin1Char(x)
64 
65 static void reloadRequestWithoutDisposition (QNetworkReply* reply)
66 {
67  QNetworkRequest req (reply->request());
68  req.setRawHeader("x-kdewebkit-ignore-disposition", "true");
69 
70  QWebFrame* frame = qobject_cast<QWebFrame*> (req.originatingObject());
71  if (!frame)
72  return;
73 
74  frame->load(req);
75 }
76 
77 static bool isMimeTypeAssociatedWithSelf(const KService::Ptr &offer)
78 {
79  if (!offer)
80  return false;
81 
82  kDebug(800) << offer->desktopEntryName();
83 
84  const QString& appName = QCoreApplication::applicationName();
85 
86  if (appName == offer->desktopEntryName() || offer->exec().trimmed().startsWith(appName))
87  return true;
88 
89  // konqueror exception since it uses kfmclient to open html content...
90  if (appName == QL1S("konqueror") && offer->exec().trimmed().startsWith(QL1S("kfmclient")))
91  return true;
92 
93  return false;
94 }
95 
96 static void extractMimeType(const QNetworkReply* reply, QString& mimeType)
97 {
98  mimeType.clear();
99  const KIO::MetaData& metaData = reply->attribute(static_cast<QNetworkRequest::Attribute>(KIO::AccessManager::MetaData)).toMap();
100  if (metaData.contains(QL1S("content-type")))
101  mimeType = metaData.value(QL1S("content-type"));
102 
103  if (!mimeType.isEmpty())
104  return;
105 
106  if (!reply->hasRawHeader("Content-Type"))
107  return;
108 
109  const QString value (QL1S(reply->rawHeader("Content-Type").simplified().constData()));
110  const int index = value.indexOf(QL1C(';'));
111  mimeType = ((index == -1) ? value : value.left(index));
112 }
113 
114 static bool downloadResource (const KUrl& srcUrl, const QString& suggestedName = QString(),
115  QWidget* parent = 0, const KIO::MetaData& metaData = KIO::MetaData())
116 {
117  const QString fileName = suggestedName.isEmpty() ? srcUrl.fileName() : suggestedName;
118  // convert filename to URL using fromPath to avoid trouble with ':' in filenames (#184202)
119  KUrl destUrl = KFileDialog::getSaveFileName(KUrl::fromPath(fileName), QString(), parent);
120  if (!destUrl.isValid())
121  return false;
122 
123  // Using KIO::copy rather than file_copy, to benefit from "dest already exists" dialogs.
124  KIO::Job *job = KIO::copy(srcUrl, destUrl);
125 
126  if (!metaData.isEmpty())
127  job->setMetaData(metaData);
128 
129  job->addMetaData(QL1S("MaxCacheSize"), QL1S("0")); // Don't store in http cache.
130  job->addMetaData(QL1S("cache"), QL1S("cache")); // Use entry from cache if available.
131  job->ui()->setWindow((parent ? parent->window() : 0));
132  job->ui()->setAutoErrorHandlingEnabled(true);
133  return true;
134 }
135 
136 static bool isReplyStatusOk(const QNetworkReply* reply)
137 {
138  if (!reply || reply->error() != QNetworkReply::NoError)
139  return false;
140 
141  // Check HTTP status code only for http and webdav protocols...
142  const QString scheme = reply->url().scheme();
143  if (scheme.startsWith(QLatin1String("http"), Qt::CaseInsensitive) ||
144  scheme.startsWith(QLatin1String("webdav"), Qt::CaseInsensitive)) {
145  bool ok = false;
146  const int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(&ok);
147  if (!ok || statusCode < 200 || statusCode > 299)
148  return false;
149  }
150 
151  return true;
152 }
153 
154 class KWebPage::KWebPagePrivate
155 {
156 public:
157  KWebPagePrivate() : inPrivateBrowsingMode(false) {}
158  void _k_copyResultToTempFile(KJob * job)
159  {
160  if ( job->error() ) {
161  job->uiDelegate()->showErrorMessage();
162  return;
163  }
164  // Same as KRun::foundMimeType but with a different URL
165  (void)KRun::runUrl(static_cast<KIO::FileCopyJob *>(job)->destUrl(), mimeType, window);
166  }
167 
168  QPointer<QWidget> window;
169  QString mimeType;
170  QPointer<KWebWallet> wallet;
171  bool inPrivateBrowsingMode;
172 };
173 
174 static void setActionIcon(QAction* action, const QIcon& icon)
175 {
176  if (action) {
177  action->setIcon(icon);
178  }
179 }
180 
181 static void setActionShortcut(QAction* action, const KShortcut& shortcut)
182 {
183  if (action) {
184  action->setShortcuts(shortcut.toList());
185  }
186 }
187 
188 KWebPage::KWebPage(QObject *parent, Integration flags)
189  :QWebPage(parent), d(new KWebPagePrivate)
190 {
191  // KDE KParts integration for <embed> tag...
192  if (!flags || (flags & KPartsIntegration))
193  setPluginFactory(new KWebPluginFactory(this));
194 
195  QWidget *parentWidget = qobject_cast<QWidget*>(parent);
196  QWidget *window = parentWidget ? parentWidget->window() : 0;
197 
198  // KDE IO (KIO) integration...
199  if (!flags || (flags & KIOIntegration)) {
200  KIO::Integration::AccessManager *manager = new KIO::Integration::AccessManager(this);
201  // Disable QtWebKit's internal cache to avoid duplication with the one in KIO...
202  manager->setCache(0);
203  manager->setWindow(window);
204  manager->setEmitReadyReadOnMetaDataChange(true);
205  setNetworkAccessManager(manager);
206  }
207 
208  // KWallet integration...
209  if (!flags || (flags & KWalletIntegration)) {
210  setWallet(new KWebWallet(0, (window ? window->winId() : 0) ));
211  }
212 
213  setActionIcon(action(Back), KIcon("go-previous"));
214  setActionIcon(action(Forward), KIcon("go-next"));
215  setActionIcon(action(Reload), KIcon("view-refresh"));
216  setActionIcon(action(Stop), KIcon("process-stop"));
217  setActionIcon(action(Cut), KIcon("edit-cut"));
218  setActionIcon(action(Copy), KIcon("edit-copy"));
219  setActionIcon(action(Paste), KIcon("edit-paste"));
220  setActionIcon(action(Undo), KIcon("edit-undo"));
221  setActionIcon(action(Redo), KIcon("edit-redo"));
222  setActionIcon(action(InspectElement), KIcon("view-process-all"));
223  setActionIcon(action(OpenLinkInNewWindow), KIcon("window-new"));
224  setActionIcon(action(OpenFrameInNewWindow), KIcon("window-new"));
225  setActionIcon(action(OpenImageInNewWindow), KIcon("window-new"));
226  setActionIcon(action(CopyLinkToClipboard), KIcon("edit-copy"));
227  setActionIcon(action(CopyImageToClipboard), KIcon("edit-copy"));
228  setActionIcon(action(ToggleBold), KIcon("format-text-bold"));
229  setActionIcon(action(ToggleItalic), KIcon("format-text-italic"));
230  setActionIcon(action(ToggleUnderline), KIcon("format-text-underline"));
231  setActionIcon(action(DownloadLinkToDisk), KIcon("document-save"));
232  setActionIcon(action(DownloadImageToDisk), KIcon("document-save"));
233 
234  settings()->setWebGraphic(QWebSettings::MissingPluginGraphic, KIcon("preferences-plugin").pixmap(32, 32));
235  settings()->setWebGraphic(QWebSettings::MissingImageGraphic, KIcon("image-missing").pixmap(32, 32));
236  settings()->setWebGraphic(QWebSettings::DefaultFrameIconGraphic, KIcon("applications-internet").pixmap(32, 32));
237 
238  setActionShortcut(action(Back), KStandardShortcut::back());
239  setActionShortcut(action(Forward), KStandardShortcut::forward());
240  setActionShortcut(action(Reload), KStandardShortcut::reload());
241  setActionShortcut(action(Stop), KShortcut(QKeySequence(Qt::Key_Escape)));
242  setActionShortcut(action(Cut), KStandardShortcut::cut());
243  setActionShortcut(action(Copy), KStandardShortcut::copy());
244  setActionShortcut(action(Paste), KStandardShortcut::paste());
245  setActionShortcut(action(Undo), KStandardShortcut::undo());
246  setActionShortcut(action(Redo), KStandardShortcut::redo());
247  setActionShortcut(action(SelectAll), KStandardShortcut::selectAll());
248 }
249 
250 KWebPage::~KWebPage()
251 {
252  delete d;
253 }
254 
255 bool KWebPage::isExternalContentAllowed() const
256 {
257  KIO::AccessManager *manager = qobject_cast<KIO::AccessManager*>(networkAccessManager());
258  if (manager)
259  return manager->isExternalContentAllowed();
260  return true;
261 }
262 
263 KWebWallet *KWebPage::wallet() const
264 {
265  return d->wallet;
266 }
267 
268 void KWebPage::setAllowExternalContent(bool allow)
269 {
270  KIO::AccessManager *manager = qobject_cast<KIO::AccessManager*>(networkAccessManager());
271  if (manager)
272  manager->setExternalContentAllowed(allow);
273 }
274 
275 void KWebPage::setWallet(KWebWallet* wallet)
276 {
277  // Delete the current wallet if this object is its parent...
278  if (d->wallet && this == d->wallet->parent())
279  delete d->wallet;
280 
281  d->wallet = wallet;
282 
283  if (d->wallet)
284  d->wallet->setParent(this);
285 }
286 
287 void KWebPage::downloadRequest(const QNetworkRequest &request)
288 {
289  downloadResource(request.url(), QString(), view(),
290  request.attribute(static_cast<QNetworkRequest::Attribute>(KIO::AccessManager::MetaData)).toMap());
291 }
292 
293 void KWebPage::downloadUrl(const KUrl &url)
294 {
295  downloadResource(url, QString(), view());
296 }
297 
298 void KWebPage::downloadResponse(QNetworkReply *reply)
299 {
300  Q_ASSERT(reply);
301 
302  if (!reply)
303  return;
304 
305  // Put the job on hold only for the protocols we know about (read: http).
306  KIO::Integration::AccessManager::putReplyOnHold(reply);
307 
308  QString mimeType;
309  KIO::MetaData metaData;
310 
311  if (handleReply(reply, &mimeType, &metaData)) {
312  return;
313  }
314 
315  const KUrl replyUrl (reply->url());
316  QWidget* topLevelWindow = view() ? view()->window() : 0;
317 
318  // Ask KRun to handle the response when mimetype is unknown
319  if (mimeType.isEmpty()) {
320  (void)new KRun(replyUrl, topLevelWindow, 0 , replyUrl.isLocalFile());
321  return;
322  }
323 
324  // Ask KRun::runUrl to handle the response when mimetype is inode/*
325  if (mimeType.startsWith(QL1S("inode/"), Qt::CaseInsensitive) &&
326  KRun::runUrl(replyUrl, mimeType, topLevelWindow, false, false,
327  metaData.value(QL1S("content-disposition-filename")))) {
328  return;
329  }
330 }
331 
332 QString KWebPage::sessionMetaData(const QString &key) const
333 {
334  QString value;
335 
336  KIO::Integration::AccessManager *manager = qobject_cast<KIO::Integration::AccessManager *>(networkAccessManager());
337  if (manager)
338  value = manager->sessionMetaData().value(key);
339 
340  return value;
341 }
342 
343 QString KWebPage::requestMetaData(const QString &key) const
344 {
345  QString value;
346 
347  KIO::Integration::AccessManager *manager = qobject_cast<KIO::Integration::AccessManager *>(networkAccessManager());
348  if (manager)
349  value = manager->requestMetaData().value(key);
350 
351  return value;
352 }
353 
354 void KWebPage::setSessionMetaData(const QString &key, const QString &value)
355 {
356  KIO::Integration::AccessManager *manager = qobject_cast<KIO::Integration::AccessManager *>(networkAccessManager());
357  if (manager)
358  manager->sessionMetaData()[key] = value;
359 }
360 
361 void KWebPage::setRequestMetaData(const QString &key, const QString &value)
362 {
363  KIO::Integration::AccessManager *manager = qobject_cast<KIO::Integration::AccessManager *>(networkAccessManager());
364  if (manager)
365  manager->requestMetaData()[key] = value;
366 }
367 
368 void KWebPage::removeSessionMetaData(const QString &key)
369 {
370  KIO::Integration::AccessManager *manager = qobject_cast<KIO::Integration::AccessManager *>(networkAccessManager());
371  if (manager)
372  manager->sessionMetaData().remove(key);
373 }
374 
375 void KWebPage::removeRequestMetaData(const QString &key)
376 {
377  KIO::Integration::AccessManager *manager = qobject_cast<KIO::Integration::AccessManager *>(networkAccessManager());
378  if (manager)
379  manager->requestMetaData().remove(key);
380 }
381 
382 QString KWebPage::userAgentForUrl(const QUrl& _url) const
383 {
384  const KUrl url(_url);
385  const QString userAgent = KProtocolManager::userAgentForHost((url.isLocalFile() ? QL1S("localhost") : url.host()));
386 
387  if (userAgent == KProtocolManager::defaultUserAgent())
388  return QWebPage::userAgentForUrl(_url);
389 
390  return userAgent;
391 }
392 
393 static void setDisableCookieJarStorage(QNetworkAccessManager* manager, bool status)
394 {
395  if (manager) {
396  KIO::Integration::CookieJar *cookieJar = manager ? qobject_cast<KIO::Integration::CookieJar*>(manager->cookieJar()) : 0;
397  if (cookieJar) {
398  //kDebug(800) << "Store cookies ?" << !status;
399  cookieJar->setDisableCookieStorage(status);
400  }
401  }
402 }
403 
404 bool KWebPage::acceptNavigationRequest(QWebFrame *frame, const QNetworkRequest &request, NavigationType type)
405 {
406  kDebug(800) << "url:" << request.url() << ", type:" << type << ", frame:" << frame;
407 
408  if (frame && d->wallet && type == QWebPage::NavigationTypeFormSubmitted)
409  d->wallet->saveFormData(frame);
410 
411  // Make sure nothing is cached when private browsing mode is enabled...
412  if (settings()->testAttribute(QWebSettings::PrivateBrowsingEnabled)) {
413  if (!d->inPrivateBrowsingMode) {
414  setDisableCookieJarStorage(networkAccessManager(), true);
415  setSessionMetaData(QL1S("no-cache"), QL1S("true"));
416  d->inPrivateBrowsingMode = true;
417  }
418  } else {
419  if (d->inPrivateBrowsingMode) {
420  setDisableCookieJarStorage(networkAccessManager(), false);
421  removeSessionMetaData(QL1S("no-cache"));
422  d->inPrivateBrowsingMode = false;
423  }
424  }
425 
426  /*
427  If the navigation request is from the main frame, set the cross-domain
428  meta-data value to the current url for proper integration with KCookieJar...
429  */
430  if (frame == mainFrame() && type != QWebPage::NavigationTypeReload)
431  setSessionMetaData(QL1S("cross-domain"), request.url().toString());
432 
433  return QWebPage::acceptNavigationRequest(frame, request, type);
434 }
435 
436 bool KWebPage::handleReply(QNetworkReply* reply, QString* contentType, KIO::MetaData* metaData)
437 {
438  // Reply url...
439  const KUrl replyUrl (reply->url());
440 
441  // Get the top level window...
442  QWidget* topLevelWindow = view() ? view()->window() : 0;
443 
444  // Get suggested file name...
445  const KIO::MetaData& data = reply->attribute(static_cast<QNetworkRequest::Attribute>(KIO::AccessManager::MetaData)).toMap();
446  const QString suggestedFileName = data.value(QL1S("content-disposition-filename"));
447  if (metaData) {
448  *metaData = data;
449  }
450 
451  // Get the mime-type...
452  QString mimeType;
453  extractMimeType(reply, mimeType);
454  if (contentType) {
455  *contentType = mimeType;
456  }
457 
458  // Let the calling function deal with handling empty or inode/* mimetypes...
459  if (mimeType.isEmpty() || mimeType.startsWith(QL1S("inode/"), Qt::CaseInsensitive)) {
460  return false;
461  }
462 
463  // Convert executable text files to plain text...
464  if (KParts::BrowserRun::isTextExecutable(mimeType))
465  mimeType = QL1S("text/plain");
466 
467  //kDebug(800) << "Content-disposition:" << suggestedFileName;
468  //kDebug(800) << "Got unsupported content of type:" << mimeType << "URL:" << replyUrl;
469  //kDebug(800) << "Error code:" << reply->error() << reply->errorString();
470 
471  if (isReplyStatusOk(reply)) {
472  KParts::BrowserOpenOrSaveQuestion::Result result;
473  KParts::BrowserOpenOrSaveQuestion dlg(topLevelWindow, replyUrl, mimeType);
474  dlg.setSuggestedFileName(suggestedFileName);
475  dlg.setFeatures(KParts::BrowserOpenOrSaveQuestion::ServiceSelection);
476  result = dlg.askOpenOrSave();
477 
478  switch (result) {
479  case KParts::BrowserOpenOrSaveQuestion::Open:
480  // Handle Post operations that return content...
481  if (reply->operation() == QNetworkAccessManager::PostOperation) {
482  d->mimeType = mimeType;
483  d->window = topLevelWindow;
484  QFileInfo finfo (suggestedFileName.isEmpty() ? replyUrl.fileName() : suggestedFileName);
485  KTemporaryFile tempFile;
486  tempFile.setSuffix(QL1C('.') + finfo.suffix());
487  tempFile.setAutoRemove(false);
488  tempFile.open();
489  KUrl destUrl;
490  destUrl.setPath(tempFile.fileName());
491  KIO::Job *job = KIO::file_copy(replyUrl, destUrl, 0600, KIO::Overwrite);
492  job->ui()->setWindow(topLevelWindow);
493  job->ui()->setAutoErrorHandlingEnabled(true);
494  connect(job, SIGNAL(result(KJob*)),
495  this, SLOT(_k_copyResultToTempFile(KJob*)));
496  return true;
497  }
498 
499  // Ask before running any executables...
500  if (KParts::BrowserRun::allowExecution(mimeType, replyUrl)) {
501  KService::Ptr offer = dlg.selectedService();
502  // HACK: The check below is necessary to break an infinite
503  // recursion that occurs whenever this function is called as a result
504  // of receiving content that can be rendered by the app using this engine.
505  // For example a text/html header that containing a content-disposition
506  // header is received by the app using this class.
507  if (isMimeTypeAssociatedWithSelf(offer)) {
508  reloadRequestWithoutDisposition(reply);
509  } else {
510  KUrl::List list;
511  list.append(replyUrl);
512  bool success = false;
513  // kDebug(800) << "Suggested file name:" << suggestedFileName;
514  if (offer) {
515  success = KRun::run(*offer, list, topLevelWindow , false, suggestedFileName);
516  } else {
517  success = KRun::displayOpenWithDialog(list, topLevelWindow, false, suggestedFileName);
518  }
519  // For non KIO apps and cancelled Open With dialog, remove slave on hold.
520  if (!success || (offer && !offer->categories().contains(QL1S("KDE")))) {
521  KIO::SimpleJob::removeOnHold(); // Remove any slave-on-hold...
522  }
523  }
524  return true;
525  }
526  // TODO: Instead of silently failing when allowExecution fails, notify
527  // the user why the requested action cannot be fulfilled...
528  break;
529  case KParts::BrowserOpenOrSaveQuestion::Save:
530  // Do not download local files...
531  if (!replyUrl.isLocalFile()) {
532  QString downloadCmd (reply->property("DownloadManagerExe").toString());
533  if (!downloadCmd.isEmpty()) {
534  downloadCmd += QLatin1Char(' ');
535  downloadCmd += KShell::quoteArg(replyUrl.url());
536  if (!suggestedFileName.isEmpty()) {
537  downloadCmd += QLatin1Char(' ');
538  downloadCmd += KShell::quoteArg(suggestedFileName);
539  }
540  // kDebug(800) << "download command:" << downloadCmd;
541  if (KRun::runCommand(downloadCmd, view()))
542  return true;
543  }
544  return downloadResource(replyUrl, suggestedFileName, topLevelWindow);
545  }
546  return true;
547  case KParts::BrowserOpenOrSaveQuestion::Cancel:
548  default:
549  return true;
550  }
551  } else {
552  KService::Ptr offer = KMimeTypeTrader::self()->preferredService(mimeType);
553  if (isMimeTypeAssociatedWithSelf(offer)) {
554  reloadRequestWithoutDisposition(reply);
555  return true;
556  }
557  }
558 
559  return false;
560 }
561 
562 #include "kwebpage.moc"
563 
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Fri Dec 7 2012 16:14:32 by doxygen 1.8.1 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KDEWebKit

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

kdelibs-4.8.5 API Reference

Skip menu "kdelibs-4.8.5 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • 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
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal