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 <kprotocolmanager.h>
35 #include <kjobuidelegate.h>
36 #include <krun.h>
37 #include <kjobwidgets.h>
38
39 #include <kstandardshortcut.h>
40 #include <QDebug>
41 #include <kshell.h>
42 #include <kmimetypetrader.h>
43 #include <kio/accessmanager.h>
44 #include <kio/job.h>
45 #include <kio/copyjob.h>
46 #include <kio/jobuidelegate.h>
47 #include <KIO/CommandLauncherJob>
48 #include <KIO/OpenUrlJob>
49 #include <kio/renamedialog.h>
50 #include <kio/scheduler.h>
51 #include <kparts/browseropenorsavequestion.h>
52
53 // Qt
54 #include <QPointer>
55 #include <QFileInfo>
56 #include <QCoreApplication>
57 #include <QAction>
58 #include <QFileDialog>
59 #include <QWebFrame>
60 #include <QNetworkReply>
61 #include <qtemporaryfile.h>
62
63 #define QL1S(x) QLatin1String(x)
64 #define QL1C(x) QLatin1Char(x)
65
reloadRequestWithoutDisposition(QNetworkReply * reply)66 static void reloadRequestWithoutDisposition(QNetworkReply *reply)
67 {
68 QNetworkRequest req(reply->request());
69 req.setRawHeader("x-kdewebkit-ignore-disposition", "true");
70
71 QWebFrame *frame = qobject_cast<QWebFrame *> (req.originatingObject());
72 if (!frame) {
73 return;
74 }
75
76 frame->load(req);
77 }
78
isMimeTypeAssociatedWithSelf(const KService::Ptr & offer)79 static bool isMimeTypeAssociatedWithSelf(const KService::Ptr &offer)
80 {
81 if (!offer) {
82 return false;
83 }
84
85 // qDebug() << offer->desktopEntryName();
86
87 const QString &appName = QCoreApplication::applicationName();
88
89 if (appName == offer->desktopEntryName() || offer->exec().trimmed().startsWith(appName)) {
90 return true;
91 }
92
93 // konqueror exception since it uses kfmclient to open html content...
94 if (appName == QL1S("konqueror") && offer->exec().trimmed().startsWith(QL1S("kfmclient"))) {
95 return true;
96 }
97
98 return false;
99 }
100
extractMimeType(const QNetworkReply * reply,QString & mimeType)101 static void extractMimeType(const QNetworkReply *reply, QString &mimeType)
102 {
103 mimeType.clear();
104 const KIO::MetaData &metaData = reply->attribute(static_cast<QNetworkRequest::Attribute>(KIO::AccessManager::MetaData)).toMap();
105 if (metaData.contains(QL1S("content-type"))) {
106 mimeType = metaData.value(QL1S("content-type"));
107 }
108
109 if (!mimeType.isEmpty()) {
110 return;
111 }
112
113 if (!reply->hasRawHeader("Content-Type")) {
114 return;
115 }
116
117 const QString value(QL1S(reply->rawHeader("Content-Type").simplified().constData()));
118 const int index = value.indexOf(QL1C(';'));
119 mimeType = ((index == -1) ? value : value.left(index));
120 }
121
downloadResource(const QUrl & srcUrl,const QString & suggestedName=QString (),QWidget * parent=nullptr,const KIO::MetaData & metaData=KIO::MetaData ())122 static bool downloadResource(const QUrl &srcUrl, const QString &suggestedName = QString(),
123 QWidget *parent = nullptr, const KIO::MetaData &metaData = KIO::MetaData())
124 {
125 const QString fileName = suggestedName.isEmpty() ? srcUrl.fileName() : suggestedName;
126 // convert filename to URL using fromPath to avoid trouble with ':' in filenames (#184202)
127 QUrl destUrl = QUrl::fromLocalFile(QFileDialog::getSaveFileName(parent, QString(), fileName));
128 if (!destUrl.isValid()) {
129 return false;
130 }
131
132 // Using KIO::copy rather than file_copy, to benefit from "dest already exists" dialogs.
133 KIO::Job *job = KIO::copy(srcUrl, destUrl);
134
135 if (!metaData.isEmpty()) {
136 job->setMetaData(metaData);
137 }
138
139 job->addMetaData(QL1S("MaxCacheSize"), QL1S("0")); // Don't store in http cache.
140 job->addMetaData(QL1S("cache"), QL1S("cache")); // Use entry from cache if available.
141 KJobWidgets::setWindow(job, parent ? parent->window() : nullptr);
142 job->uiDelegate()->setAutoErrorHandlingEnabled(true);
143 return true;
144 }
145
isReplyStatusOk(const QNetworkReply * reply)146 static bool isReplyStatusOk(const QNetworkReply *reply)
147 {
148 if (!reply || reply->error() != QNetworkReply::NoError) {
149 return false;
150 }
151
152 // Check HTTP status code only for http and webdav protocols...
153 const QString scheme = reply->url().scheme();
154 if (scheme.startsWith(QLatin1String("http"), Qt::CaseInsensitive) ||
155 scheme.startsWith(QLatin1String("webdav"), Qt::CaseInsensitive)) {
156 bool ok = false;
157 const int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(&ok);
158 if (!ok || statusCode < 200 || statusCode > 299) {
159 return false;
160 }
161 }
162
163 return true;
164 }
165
166 class KWebPage::KWebPagePrivate
167 {
168 public:
KWebPagePrivate(KWebPage * page)169 KWebPagePrivate(KWebPage *page)
170 : q(page)
171 , inPrivateBrowsingMode(false)
172 {
173 }
174
windowWidget()175 QWidget *windowWidget()
176 {
177 return (window ? window.data() : q->view());
178 }
179
_k_copyResultToTempFile(KJob * job)180 void _k_copyResultToTempFile(KJob *job)
181 {
182 KIO::FileCopyJob *cJob = qobject_cast<KIO::FileCopyJob *>(job);
183 if (cJob && !cJob->error()) {
184 // Same as KRun::foundMimeType but with a different URL
185 auto *job = new KIO::OpenUrlJob(cJob->destUrl(), mimeType);
186 job->setUiDelegate(new KIO::JobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, window));
187 job->start();
188 }
189 }
190
_k_receivedContentType(KIO::Job * job,const QString & mimetype)191 void _k_receivedContentType(KIO::Job *job, const QString &mimetype)
192 {
193 KIO::TransferJob *tJob = qobject_cast<KIO::TransferJob *>(job);
194 if (tJob && !tJob->error()) {
195 tJob->putOnHold();
196 KIO::Scheduler::publishSlaveOnHold();
197 // Get suggested file name...
198 mimeType = mimetype;
199 const QString suggestedFileName(tJob->queryMetaData(QL1S("content-disposition-filename")));
200 // qDebug() << "suggested filename:" << suggestedFileName << ", mimetype:" << mimetype;
201 (void) downloadResource(tJob->url(), suggestedFileName, window, tJob->metaData());
202 }
203 }
204
_k_contentTypeCheckFailed(KJob * job)205 void _k_contentTypeCheckFailed(KJob *job)
206 {
207 KIO::TransferJob *tJob = qobject_cast<KIO::TransferJob *>(job);
208 // On error simply call downloadResource which will probably fail as well.
209 if (tJob && tJob->error()) {
210 (void)downloadResource(tJob->url(), QString(), window, tJob->metaData());
211 }
212 }
213
214 KWebPage *q;
215 QPointer<QWidget> window;
216 QString mimeType;
217 QPointer<KWebWallet> wallet;
218 bool inPrivateBrowsingMode;
219 };
220
setActionIcon(QAction * action,const QIcon & icon)221 static void setActionIcon(QAction *action, const QIcon &icon)
222 {
223 if (action) {
224 action->setIcon(icon);
225 }
226 }
227
setActionShortcut(QAction * action,const QList<QKeySequence> & shortcut)228 static void setActionShortcut(QAction *action, const QList<QKeySequence> &shortcut)
229 {
230 if (action) {
231 action->setShortcuts(shortcut);
232 }
233 }
234
KWebPage(QObject * parent,Integration flags)235 KWebPage::KWebPage(QObject *parent, Integration flags)
236 : QWebPage(parent), d(new KWebPagePrivate(this))
237 {
238 // KDE KParts integration for <embed> tag...
239 if (!flags || (flags & KPartsIntegration)) {
240 setPluginFactory(new KWebPluginFactory(this));
241 }
242
243 QWidget *parentWidget = qobject_cast<QWidget *>(parent);
244 d->window = (parentWidget ? parentWidget->window() : nullptr);
245
246 // KDE IO (KIO) integration...
247 if (!flags || (flags & KIOIntegration)) {
248 KIO::Integration::AccessManager *manager = new KIO::Integration::AccessManager(this);
249 // Disable QtWebKit's internal cache to avoid duplication with the one in KIO...
250 manager->setCache(nullptr);
251 manager->setWindow(d->window);
252 manager->setEmitReadyReadOnMetaDataChange(true);
253 setNetworkAccessManager(manager);
254 }
255
256 // KWallet integration...
257 if (!flags || (flags & KWalletIntegration)) {
258 setWallet(new KWebWallet(nullptr, (d->window ? d->window->winId() : 0)));
259 }
260
261 setActionIcon(action(Back), QIcon::fromTheme("go-previous"));
262 setActionIcon(action(Forward), QIcon::fromTheme("go-next"));
263 setActionIcon(action(Reload), QIcon::fromTheme("view-refresh"));
264 setActionIcon(action(Stop), QIcon::fromTheme("process-stop"));
265 setActionIcon(action(Cut), QIcon::fromTheme("edit-cut"));
266 setActionIcon(action(Copy), QIcon::fromTheme("edit-copy"));
267 setActionIcon(action(Paste), QIcon::fromTheme("edit-paste"));
268 setActionIcon(action(Undo), QIcon::fromTheme("edit-undo"));
269 setActionIcon(action(Redo), QIcon::fromTheme("edit-redo"));
270 setActionIcon(action(SelectAll), QIcon::fromTheme("edit-select-all"));
271 setActionIcon(action(InspectElement), QIcon::fromTheme("view-process-all"));
272 setActionIcon(action(OpenLinkInNewWindow), QIcon::fromTheme("window-new"));
273 setActionIcon(action(OpenFrameInNewWindow), QIcon::fromTheme("window-new"));
274 setActionIcon(action(OpenImageInNewWindow), QIcon::fromTheme("window-new"));
275 setActionIcon(action(CopyLinkToClipboard), QIcon::fromTheme("edit-copy"));
276 setActionIcon(action(CopyImageToClipboard), QIcon::fromTheme("edit-copy"));
277 setActionIcon(action(ToggleBold), QIcon::fromTheme("format-text-bold"));
278 setActionIcon(action(ToggleItalic), QIcon::fromTheme("format-text-italic"));
279 setActionIcon(action(ToggleUnderline), QIcon::fromTheme("format-text-underline"));
280 setActionIcon(action(DownloadLinkToDisk), QIcon::fromTheme("document-save"));
281 setActionIcon(action(DownloadImageToDisk), QIcon::fromTheme("document-save"));
282
283 settings()->setWebGraphic(QWebSettings::MissingPluginGraphic, QIcon::fromTheme("preferences-plugin").pixmap(32, 32));
284 settings()->setWebGraphic(QWebSettings::MissingImageGraphic, QIcon::fromTheme("image-missing").pixmap(32, 32));
285 settings()->setWebGraphic(QWebSettings::DefaultFrameIconGraphic, QIcon::fromTheme("applications-internet").pixmap(32, 32));
286
287 setActionShortcut(action(Back), KStandardShortcut::back());
288 setActionShortcut(action(Forward), KStandardShortcut::forward());
289 setActionShortcut(action(Reload), KStandardShortcut::reload());
290 setActionShortcut(action(Stop), QList<QKeySequence>() << QKeySequence(Qt::Key_Escape));
291 setActionShortcut(action(Cut), KStandardShortcut::cut());
292 setActionShortcut(action(Copy), KStandardShortcut::copy());
293 setActionShortcut(action(Paste), KStandardShortcut::paste());
294 setActionShortcut(action(Undo), KStandardShortcut::undo());
295 setActionShortcut(action(Redo), KStandardShortcut::redo());
296 setActionShortcut(action(SelectAll), KStandardShortcut::selectAll());
297 }
298
~KWebPage()299 KWebPage::~KWebPage()
300 {
301 delete d;
302 }
303
isExternalContentAllowed() const304 bool KWebPage::isExternalContentAllowed() const
305 {
306 KIO::AccessManager *manager = qobject_cast<KIO::AccessManager *>(networkAccessManager());
307 if (manager) {
308 return manager->isExternalContentAllowed();
309 }
310 return true;
311 }
312
wallet() const313 KWebWallet *KWebPage::wallet() const
314 {
315 return d->wallet;
316 }
317
setAllowExternalContent(bool allow)318 void KWebPage::setAllowExternalContent(bool allow)
319 {
320 KIO::AccessManager *manager = qobject_cast<KIO::AccessManager *>(networkAccessManager());
321 if (manager) {
322 manager->setExternalContentAllowed(allow);
323 }
324 }
325
setWallet(KWebWallet * wallet)326 void KWebPage::setWallet(KWebWallet *wallet)
327 {
328 // Delete the current wallet if this object is its parent...
329 if (d->wallet && this == d->wallet->parent()) {
330 delete d->wallet;
331 }
332
333 d->wallet = wallet;
334
335 if (d->wallet) {
336 d->wallet->setParent(this);
337 }
338 }
339
downloadRequest(const QNetworkRequest & request)340 void KWebPage::downloadRequest(const QNetworkRequest &request)
341 {
342 KIO::TransferJob *job = KIO::get(request.url());
343 connect(job, &KIO::TransferJob::mimeTypeFound,
344 this, [this](KIO::Job *job, const QString &mimeType) { d->_k_receivedContentType(job, mimeType); });
345
346 job->setMetaData(request.attribute(static_cast<QNetworkRequest::Attribute>(KIO::AccessManager::MetaData)).toMap());
347 job->addMetaData(QL1S("MaxCacheSize"), QL1S("0")); // Don't store in http cache.
348 job->addMetaData(QL1S("cache"), QL1S("cache")); // Use entry from cache if available.
349 KJobWidgets::setWindow(job, d->windowWidget());
350 }
351
downloadUrl(const QUrl & url)352 void KWebPage::downloadUrl(const QUrl &url)
353 {
354 downloadRequest(QNetworkRequest(url));
355 }
356
downloadResponse(QNetworkReply * reply)357 void KWebPage::downloadResponse(QNetworkReply *reply)
358 {
359 Q_ASSERT(reply);
360
361 if (!reply) {
362 return;
363 }
364
365 // Put the job on hold only for the protocols we know about (read: http).
366 KIO::Integration::AccessManager::putReplyOnHold(reply);
367
368 QString mimeType;
369 KIO::MetaData metaData;
370
371 if (handleReply(reply, &mimeType, &metaData)) {
372 return;
373 }
374
375 // Ask OpenUrlJob to handle the response when mimetype is unknown
376 // or when mimetype is inode/*
377 if (mimeType.isEmpty() || mimeType.startsWith(QL1S("inode/"), Qt::CaseInsensitive)) {
378 auto *job = new KIO::OpenUrlJob(reply->url(), mimeType);
379 job->setSuggestedFileName(metaData.value(QL1S("content-disposition-filename")));
380 job->setUiDelegate(new KIO::JobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, d->windowWidget()));
381 job->start();
382 return;
383 }
384 }
385
sessionMetaData(const QString & key) const386 QString KWebPage::sessionMetaData(const QString &key) const
387 {
388 QString value;
389
390 KIO::Integration::AccessManager *manager = qobject_cast<KIO::Integration::AccessManager *>(networkAccessManager());
391 if (manager) {
392 value = manager->sessionMetaData().value(key);
393 }
394
395 return value;
396 }
397
requestMetaData(const QString & key) const398 QString KWebPage::requestMetaData(const QString &key) const
399 {
400 QString value;
401
402 KIO::Integration::AccessManager *manager = qobject_cast<KIO::Integration::AccessManager *>(networkAccessManager());
403 if (manager) {
404 value = manager->requestMetaData().value(key);
405 }
406
407 return value;
408 }
409
setSessionMetaData(const QString & key,const QString & value)410 void KWebPage::setSessionMetaData(const QString &key, const QString &value)
411 {
412 KIO::Integration::AccessManager *manager = qobject_cast<KIO::Integration::AccessManager *>(networkAccessManager());
413 if (manager) {
414 manager->sessionMetaData()[key] = value;
415 }
416 }
417
setRequestMetaData(const QString & key,const QString & value)418 void KWebPage::setRequestMetaData(const QString &key, const QString &value)
419 {
420 KIO::Integration::AccessManager *manager = qobject_cast<KIO::Integration::AccessManager *>(networkAccessManager());
421 if (manager) {
422 manager->requestMetaData()[key] = value;
423 }
424 }
425
removeSessionMetaData(const QString & key)426 void KWebPage::removeSessionMetaData(const QString &key)
427 {
428 KIO::Integration::AccessManager *manager = qobject_cast<KIO::Integration::AccessManager *>(networkAccessManager());
429 if (manager) {
430 manager->sessionMetaData().remove(key);
431 }
432 }
433
removeRequestMetaData(const QString & key)434 void KWebPage::removeRequestMetaData(const QString &key)
435 {
436 KIO::Integration::AccessManager *manager = qobject_cast<KIO::Integration::AccessManager *>(networkAccessManager());
437 if (manager) {
438 manager->requestMetaData().remove(key);
439 }
440 }
441
userAgentForUrl(const QUrl & _url) const442 QString KWebPage::userAgentForUrl(const QUrl &_url) const
443 {
444 const QUrl url(_url);
445 const QString userAgent = KProtocolManager::userAgentForHost((url.isLocalFile() ? QL1S("localhost") : url.host()));
446
447 if (userAgent == KProtocolManager::defaultUserAgent()) {
448 return QWebPage::userAgentForUrl(_url);
449 }
450
451 return userAgent;
452 }
453
setDisableCookieJarStorage(QNetworkAccessManager * manager,bool status)454 static void setDisableCookieJarStorage(QNetworkAccessManager *manager, bool status)
455 {
456 if (manager) {
457 KIO::Integration::CookieJar *cookieJar = manager ? qobject_cast<KIO::Integration::CookieJar *>(manager->cookieJar()) : nullptr;
458 if (cookieJar) {
459 //qDebug() << "Store cookies ?" << !status;
460 cookieJar->setDisableCookieStorage(status);
461 }
462 }
463 }
464
acceptNavigationRequest(QWebFrame * frame,const QNetworkRequest & request,NavigationType type)465 bool KWebPage::acceptNavigationRequest(QWebFrame *frame, const QNetworkRequest &request, NavigationType type)
466 {
467 // qDebug() << "url:" << request.url() << ", type:" << type << ", frame:" << frame;
468
469 if (frame && d->wallet && type == QWebPage::NavigationTypeFormSubmitted) {
470 d->wallet->saveFormData(frame);
471 }
472
473 // Make sure nothing is cached when private browsing mode is enabled...
474 if (settings()->testAttribute(QWebSettings::PrivateBrowsingEnabled)) {
475 if (!d->inPrivateBrowsingMode) {
476 setDisableCookieJarStorage(networkAccessManager(), true);
477 setSessionMetaData(QL1S("no-cache"), QL1S("true"));
478 d->inPrivateBrowsingMode = true;
479 }
480 } else {
481 if (d->inPrivateBrowsingMode) {
482 setDisableCookieJarStorage(networkAccessManager(), false);
483 removeSessionMetaData(QL1S("no-cache"));
484 d->inPrivateBrowsingMode = false;
485 }
486 }
487
488 /*
489 If the navigation request is from the main frame, set the cross-domain
490 meta-data value to the current url for proper integration with KCookieJar...
491 */
492 if (frame == mainFrame() && type != QWebPage::NavigationTypeReload) {
493 setSessionMetaData(QL1S("cross-domain"), request.url().toString());
494 }
495
496 return QWebPage::acceptNavigationRequest(frame, request, type);
497 }
498
handleReply(QNetworkReply * reply,QString * contentType,KIO::MetaData * metaData)499 bool KWebPage::handleReply(QNetworkReply *reply, QString *contentType, KIO::MetaData *metaData)
500 {
501 // Reply url...
502 const QUrl replyUrl(reply->url());
503
504 // Get suggested file name...
505 const KIO::MetaData &data = reply->attribute(static_cast<QNetworkRequest::Attribute>(KIO::AccessManager::MetaData)).toMap();
506 const QString suggestedFileName = data.value(QL1S("content-disposition-filename"));
507 if (metaData) {
508 *metaData = data;
509 }
510
511 // Get the mime-type...
512 QString mimeType;
513 extractMimeType(reply, mimeType);
514 if (contentType) {
515 *contentType = mimeType;
516 }
517
518 // Let the calling function deal with handling empty or inode/* mimetypes...
519 if (mimeType.isEmpty() || mimeType.startsWith(QL1S("inode/"), Qt::CaseInsensitive)) {
520 return false;
521 }
522
523 // Convert executable text files to plain text...
524 if (KParts::BrowserRun::isTextExecutable(mimeType)) {
525 mimeType = QL1S("text/plain");
526 }
527
528 //qDebug() << "Content-disposition:" << suggestedFileName;
529 //qDebug() << "Got unsupported content of type:" << mimeType << "URL:" << replyUrl;
530 //qDebug() << "Error code:" << reply->error() << reply->errorString();
531
532 if (isReplyStatusOk(reply)) {
533 while (true) {
534 KParts::BrowserOpenOrSaveQuestion::Result result;
535 KParts::BrowserOpenOrSaveQuestion dlg(d->windowWidget(), replyUrl, mimeType);
536 dlg.setSuggestedFileName(suggestedFileName);
537 dlg.setFeatures(KParts::BrowserOpenOrSaveQuestion::ServiceSelection);
538 result = dlg.askOpenOrSave();
539
540 switch (result) {
541 case KParts::BrowserOpenOrSaveQuestion::Open:
542 // Handle Post operations that return content...
543 if (reply->operation() == QNetworkAccessManager::PostOperation) {
544 d->mimeType = mimeType;
545 QFileInfo finfo(suggestedFileName.isEmpty() ? replyUrl.fileName() : suggestedFileName);
546 QTemporaryFile tempFile(QDir::tempPath() + QLatin1String("/kwebpage_XXXXXX.") + finfo.suffix());
547 tempFile.setAutoRemove(false);
548 tempFile.open();
549 const QUrl destUrl = QUrl::fromLocalFile(tempFile.fileName());
550 KIO::Job *job = KIO::file_copy(replyUrl, destUrl, 0600, KIO::Overwrite);
551 KJobWidgets::setWindow(job, d->windowWidget());
552 job->uiDelegate()->setAutoErrorHandlingEnabled(true);
553 connect(job, SIGNAL(result(KJob*)),
554 this, SLOT(_k_copyResultToTempFile(KJob*)));
555 return true;
556 }
557
558 // Ask before running any executables...
559 if (KParts::BrowserRun::allowExecution(mimeType, replyUrl)) {
560 KService::Ptr offer = dlg.selectedService();
561 // HACK: The check below is necessary to break an infinite
562 // recursion that occurs whenever this function is called as a result
563 // of receiving content that can be rendered by the app using this engine.
564 // For example a text/html header that containing a content-disposition
565 // header is received by the app using this class.
566 if (isMimeTypeAssociatedWithSelf(offer)) {
567 reloadRequestWithoutDisposition(reply);
568 } else {
569 QList<QUrl> list;
570 list.append(replyUrl);
571 bool success = false;
572 // qDebug() << "Suggested file name:" << suggestedFileName;
573 if (offer) {
574 success = KRun::runService(*offer, list, d->windowWidget(), false, suggestedFileName);
575 } else {
576 success = KRun::displayOpenWithDialog(list, d->windowWidget(), false, suggestedFileName);
577 if (!success) {
578 break;
579 }
580 }
581 // For non KIO apps and cancelled Open With dialog, remove slave on hold.
582 if (!success || (offer && !offer->categories().contains(QL1S("KDE")))) {
583 KIO::SimpleJob::removeOnHold(); // Remove any slave-on-hold...
584 }
585 }
586 return true;
587 }
588 // TODO: Instead of silently failing when allowExecution fails, notify
589 // the user why the requested action cannot be fulfilled...
590 return false;
591 case KParts::BrowserOpenOrSaveQuestion::Save:
592 // Do not download local files...
593 if (!replyUrl.isLocalFile()) {
594 QString downloadCmd(reply->property("DownloadManagerExe").toString());
595 if (!downloadCmd.isEmpty()) {
596 downloadCmd += QLatin1Char(' ');
597 downloadCmd += KShell::quoteArg(replyUrl.url());
598 if (!suggestedFileName.isEmpty()) {
599 downloadCmd += QLatin1Char(' ');
600 downloadCmd += KShell::quoteArg(suggestedFileName);
601 }
602 // qDebug() << "download command:" << downloadCmd;
603 if (KRun::runCommand(downloadCmd, view())) {
604 return true;
605 }
606 }
607 if (!downloadResource(replyUrl, suggestedFileName, d->windowWidget())) {
608 return true; // file dialog was cancelled, stop here
609 }
610 }
611 return true;
612 case KParts::BrowserOpenOrSaveQuestion::Cancel:
613 default:
614 KIO::SimpleJob::removeOnHold(); // Remove any slave-on-hold...
615 return true;
616 }
617 }
618 } else {
619 KService::Ptr offer = KMimeTypeTrader::self()->preferredService(mimeType);
620 if (isMimeTypeAssociatedWithSelf(offer)) {
621 reloadRequestWithoutDisposition(reply);
622 return true;
623 }
624 }
625
626 return false;
627 }
628
629 #include "moc_kwebpage.cpp"
630
631