Commit 028955b8 authored by Stefano Crocco's avatar Stefano Crocco Committed by David Faure
Browse files

Avoid possible endless loops when opening URLs

parent fd8ef32e
......@@ -660,6 +660,7 @@ void KonqMainWindow::openUrl(KonqView *_view, const QUrl &_url,
//qCDebug(KONQUEROR_LOG) << "trying openView for" << url << "( mimeType" << mimeType << ")";
if (hasMimeType || KonqUrl::isValidNotBlank(url)) {
req.args.metaData().insert("urlRequestedByApp", QString());
// Built-in view ?
if (!openView(mimeType, url, view /* can be 0 */, req)) {
//qCDebug(KONQUEROR_LOG) << "openView returned false";
......
......@@ -45,7 +45,10 @@ KonqRun::KonqRun(KonqMainWindow *mainWindow, KonqView *_childView,
// Don't use inline errors on reloading due to auto-refresh sites, but use them in all other cases
// (no reload or user-requested reload)
!req.args.reload() || req.userRequestedReload),
m_pMainWindow(mainWindow), m_pView(_childView), m_bFoundMimeType(false), m_req(req), m_inlineErrors(!req.args.reload() || req.userRequestedReload)
m_pMainWindow(mainWindow), m_pView(_childView), m_bFoundMimeType(false),
m_req(req), m_inlineErrors(!req.args.reload() || req.userRequestedReload),
m_alreadyProcessedByWebEngine(req.args.metaData().contains("AlreadyProcessedByWebEngine")),
m_automaticallyAssignedToWebEngine(false)
{
setEnableExternalBrowser(false);
//qCDebug(KONQUEROR_LOG) << "KonqRun::KonqRun() " << this;
......@@ -85,6 +88,9 @@ void KonqRun::foundMimeType(const QString &_type)
// Grab the args back from BrowserRun
m_req.args = arguments();
m_req.browserArgs = browserArguments();
if (!m_automaticallyAssignedToWebEngine) {
m_req.args.metaData().insert("urlRequestedByApp", QString());
}
bool tryEmbed = true;
// One case where we shouldn't try to embed, is when the server asks us to save
......@@ -219,12 +225,13 @@ void KonqRun::init()
return;
}
if (url.scheme().startsWith(QLatin1String("http")) && usingWebEngine()) {
if (url.scheme().startsWith(QLatin1String("http")) && usingWebEngine() && !m_alreadyProcessedByWebEngine) {
//This is a fake mimetype, needed only to ensure that the URL will be handled
//by WebEnginePart which will then determine the real mimetype. If it's
//a mimetype it can't handle, it'll emit the KParts::BrowserExtension::openUrlRequest
//passing the real mimetype. Knowing the mimetype, KonqMainWindow::openUrl will handle
//it correctly without needing to use KonqRun again.
m_automaticallyAssignedToWebEngine = true;
mimeTypeDetermined(QStringLiteral("text/html"));
} else if (url.isLocalFile()
&& (url.host().isEmpty() || (url.host() == QLatin1String("localhost"))
......@@ -341,7 +348,8 @@ void KonqRun::scanFile()
// To avoid this we assume that the creator of the KonqRun has set m_alreadyProcessedByWebEngine if the URL has
// already been passed to WebEnginePart: it means that it couldn't find a suitable mimetype and we need to do it
// by ourselves, even if it means doing a double GET request.
if (m_req.args.mimeType().isEmpty() && (url().scheme() == "http" || url().scheme() == "https") && usingWebEngine()) {
if (m_req.args.mimeType().isEmpty() && url().scheme().startsWith("http") && usingWebEngine() && !m_alreadyProcessedByWebEngine) {
m_automaticallyAssignedToWebEngine = true;
mimeTypeDetermined("text/html");
return;
}
......
......@@ -78,7 +78,9 @@ private:
bool m_bFoundMimeType;
KonqOpenURLRequest m_req;
QUrl m_mailto;
bool m_inlineErrors;
bool m_inlineErrors=true;
bool m_alreadyProcessedByWebEngine=false;
bool m_automaticallyAssignedToWebEngine=false;
};
#endif // KONQRUN_H
......@@ -39,6 +39,7 @@
#include <KUserTimestamp>
#include <KPasswdServerClient>
#include <KParts/BrowserInterface>
#include <KJobWidgets>
#include <QStandardPaths>
#include <QDesktopWidget>
......@@ -138,8 +139,9 @@ static void checkForDownloadManager(QWidget* widget, QString& cmd)
cmd = exeName;
}
void WebEnginePage::download(const QUrl& url, const QString& mimetype, bool newWindow)
void WebEnginePage::download(QWebEngineDownloadItem *it, bool newWindow)
{
QUrl url = it->url();
// Integration with a download manager...
if (!url.isLocalFile()) {
QString managerExe;
......@@ -155,8 +157,49 @@ void WebEnginePage::download(const QUrl& url, const QString& mimetype, bool newW
KParts::BrowserArguments bArgs;
bArgs.setForcesNewWindow(newWindow);
KParts::OpenUrlArguments urlArgs;
urlArgs.setMimeType(mimetype);
emit part()->browserExtension()->openUrlRequest(url, urlArgs, bArgs);
urlArgs.setMimeType(it->mimeType());
urlArgs.metaData().insert("AlreadyProcessedByWebEngine", QString());
if (m_urlRequestedByApp != url) {
emit part()->browserExtension()->openUrlRequest(url, urlArgs, bArgs);
} else { //Don't ask the application to handle the URL, since the application itself asked the part to display it
saveUrlToDisk(it);
}
}
void WebEnginePage::saveUrlToDisk(QWebEngineDownloadItem* it)
{
QUrl url = it->url();
QFileDialog *dlg = new QFileDialog(view());
dlg->setAcceptMode(QFileDialog::AcceptSave);
dlg->setWindowTitle(i18n("Save As"));
dlg->setOption(QFileDialog::DontConfirmOverwrite, false);
dlg->setAttribute(Qt::WA_DeleteOnClose);
QString suggestedName;
#ifdef WEBENGINEDOWNLOADITEM_USE_PATH
suggestedName = it->suggestedFileName();
#endif
if (suggestedName.isEmpty()) {
suggestedName = it->url().fileName();
}
dlg->selectFile(suggestedName);
dlg->setDirectory(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation));
auto savePrc = [this, dlg, url](){
QUrl destUrl = dlg->selectedUrls().value(0);
if (destUrl.isValid()) {
saveUrlUsingKIO(url, destUrl);
}
};
connect(dlg, &QDialog::accepted, dlg, savePrc);
dlg->show();
}
void WebEnginePage::saveUrlUsingKIO(const QUrl& origUrl, const QUrl& destUrl)
{
KIO::FileCopyJob *job = KIO::file_copy(origUrl, destUrl, -1, KIO::Overwrite);
job->addMetaData(QStringLiteral("MaxCacheSize"), QStringLiteral("0")); // Don't store in http cache.
job->addMetaData(QStringLiteral("cache"), QStringLiteral("cache")); // Use entry from cache if available.
KJobWidgets::setWindow(job, view());
job->uiDelegate()->setAutoErrorHandlingEnabled(true);
}
void WebEnginePage::requestOpenFileAsTemporary(const QUrl& url, const QString &mimeType, bool newWindow)
......@@ -210,17 +253,19 @@ static bool domainSchemeMatch(const QUrl& u1, const QUrl& u2)
bool WebEnginePage::acceptNavigationRequest(const QUrl& url, NavigationType type, bool isMainFrame)
{
if (m_urlRequestedByApp != url) {
bool urlRequestedByApp = m_urlRequestedByApp == url;
if (!urlRequestedByApp) {
m_urlRequestedByApp = QUrl();
}
//Don't open local files using WebEnginePart except if configured to do so by the user. For example
//for example, this ensures that the "Home" link in the introduction page is opened in Dolphin part
//(or whichever part the user has chosen to open directories instead of WebEnginePart
if (url.isLocalFile()) {
emit m_part->browserExtension()->openUrlRequest(url);
return false;
}
//Don't open local files using WebEnginePart except if configured to do so by the user. For example
//for example, this ensures that the "Home" link in the introduction page is opened in Dolphin part
//(or whichever part the user has chosen to open directories instead of WebEnginePart
if (!urlRequestedByApp && url.isLocalFile()) {
emit m_part->browserExtension()->openUrlRequest(url);
return false;
}
// qCDebug(WEBENGINEPART_LOG) << url << "type=" << type;
QUrl reqUrl(url);
......
......@@ -50,7 +50,7 @@ public:
*/
void setSslInfo (const WebSslInfo &other);
void download(const QUrl &url, const QString &mimetype, bool newWindow = false);
void download(QWebEngineDownloadItem *it, bool newWindow = false);
void requestOpenFileAsTemporary(const QUrl &url, const QString &mimeType = "", bool newWindow = false);
......@@ -144,6 +144,29 @@ private:
*/
void handleCertificateError(WebEnginePartCertificateErrorDlg *dlg);
/**
* @brief Function called when the part is forced to save an URL to disk.
*
* This function should never be needed because the URL should be handled by the application itself.
* However, there can be cases in which this doesn't happen, in particular
* if there's something wrong with the system configuration and the application asks the part to
* handle something it can't display. In this case, it would work as follow:
* - the application asks the part to display the URL
* - the part can't display the URL, so a download is triggered, causing the openUrlRequest signal
* to be emitted
* - the application receives the signal and decides that the part should handle the URL
* - the part can't display the URL and triggers a download
* - endless loop
*
* To avoid this situation, inside download(), the part checks whether it was asked to handle
* the URL by the application. In that case, it doesn't emit the openUrlRequest signal
* but calls this function to directly download the file to disk.
*
* @param it the item describing the download
*/
void saveUrlToDisk(QWebEngineDownloadItem *it);
void saveUrlUsingKIO(const QUrl &origUrl, const QUrl &destUrl);
private:
enum WebEnginePageSecurity { PageUnencrypted, PageEncrypted, PageMixed };
......
......@@ -66,7 +66,7 @@ void WebEnginePartDownloadManager::performDownload(QWebEngineDownloadItem* it)
return;
}
if (it->url().scheme() != "blob") {
page->download(it->url(), it->mimeType(), forceNew);
page->download(it, forceNew);
} else {
downloadBlob(it);
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment