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