diff --git a/webenginepart/src/CMakeLists.txt b/webenginepart/src/CMakeLists.txt --- a/webenginepart/src/CMakeLists.txt +++ b/webenginepart/src/CMakeLists.txt @@ -13,6 +13,7 @@ webhistoryinterface.cpp webenginepartdownloadmanager.cpp webenginewallet.cpp + webengineparterrorschemehandler.cpp settings/webenginesettings.cpp settings/webengine_filter.cpp ui/searchbar.cpp @@ -33,6 +34,7 @@ KF5::SonnetCore KF5::WindowSystem # for KUserTimestamp KF5::Wallet + KF5::IconThemes #for KIconLoader used by WebEnginePartErrorSchemeHandler ) target_include_directories(kwebenginepartlib PUBLIC @@ -50,3 +52,5 @@ install(FILES webenginepart.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR}) install(FILES webenginepart.rc DESTINATION ${KDE_INSTALL_KXMLGUI5DIR}/webenginepart) + +install(FILES error.html DESTINATION ${KDE_INSTALL_DATADIR}/webenginepart) diff --git a/webenginepart/src/error.html b/webenginepart/src/error.html new file mode 100644 --- /dev/null +++ b/webenginepart/src/error.html @@ -0,0 +1,61 @@ + + +%1 + + + +
+
+ warning icon +%4 +
+
+ + diff --git a/webenginepart/src/webenginepart.h b/webenginepart/src/webenginepart.h --- a/webenginepart/src/webenginepart.h +++ b/webenginepart/src/webenginepart.h @@ -43,6 +43,7 @@ class KUrlLabel; class WebEngineBrowserExtension; class WebEngineWallet; +class WebEngineErrorSchemeHandler; /** * A KPart wrapper for the QtWebEngine's browser rendering engine. diff --git a/webenginepart/src/webenginepart.cpp b/webenginepart/src/webenginepart.cpp --- a/webenginepart/src/webenginepart.cpp +++ b/webenginepart/src/webenginepart.cpp @@ -35,6 +35,7 @@ #include "websslinfo.h" #include "webhistoryinterface.h" #include "webenginewallet.h" +#include "webengineparterrorschemehandler.h" #include "ui/searchbar.h" #include "ui/passwordbar.h" @@ -71,6 +72,7 @@ #include #include #include "utils.h" +#include WebEnginePart::WebEnginePart(QWidget *parentWidget, QObject *parent, const QByteArray& cachedHistory, const QStringList& /*args*/) @@ -84,6 +86,10 @@ m_featurePermissionBar(0), m_wallet(Q_NULLPTR) { + QWebEngineProfile *prof = QWebEngineProfile::defaultProfile(); + if (!prof->urlSchemeHandler("error")) { + prof->installUrlSchemeHandler("error", new WebEnginePartErrorSchemeHandler(prof)); + } KAboutData about = KAboutData(QStringLiteral("webenginepart"), i18nc("Program Name", "WebEnginePart"), /*version*/ QStringLiteral("1.3.0"), diff --git a/webenginepart/src/webengineparterrorschemehandler.h b/webenginepart/src/webengineparterrorschemehandler.h new file mode 100644 --- /dev/null +++ b/webenginepart/src/webengineparterrorschemehandler.h @@ -0,0 +1,59 @@ +/* + * This file is part of the KDE project. + * + * Copyright (C) 2018 Stefano Crocco + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * +*/ + +#ifndef WEBENGINEPARTERRORSCHEMEHANDLER_H +#define WEBENGINEPARTERRORSCHEMEHANDLER_H + +#include +#include +#include + +class QBuffer; + +class WebEnginePartErrorSchemeHandler : public QWebEngineUrlSchemeHandler{ + + Q_OBJECT + +public: + + WebEnginePartErrorSchemeHandler(QObject *parent = Q_NULLPTR); + + ~WebEnginePartErrorSchemeHandler(){} + + void requestStarted(QWebEngineUrlRequestJob * job) Q_DECL_OVERRIDE; + +private: + + struct ErrorInfo{ + int code; + QString text; + QUrl requestUrl; + }; + + ErrorInfo parseErrorUrl(const QUrl& url); + + void writeErrorPage(QBuffer *buf, const ErrorInfo &info); + + QString readWarningIconData() const; + + const QString m_warningIconData; +}; + +#endif //WEBENGINEPARTERRORSCHEMEHANDLER_H diff --git a/webenginepart/src/webengineparterrorschemehandler.cpp b/webenginepart/src/webengineparterrorschemehandler.cpp new file mode 100644 --- /dev/null +++ b/webenginepart/src/webengineparterrorschemehandler.cpp @@ -0,0 +1,174 @@ +/* + * This file is part of the KDE project. + * + * Copyright (C) 2018 Stefano Crocco + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * +*/ + +#include "webengineparterrorschemehandler.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "utils.h" + +WebEnginePartErrorSchemeHandler::WebEnginePartErrorSchemeHandler(QObject* parent): + QWebEngineUrlSchemeHandler(parent), m_warningIconData(readWarningIconData()) +{ +} + +QString WebEnginePartErrorSchemeHandler::readWarningIconData() const +{ + QString data; + QString path = KIconLoader::global()->iconPath("dialog-warning", -KIconLoader::SizeHuge); + if (path.isEmpty()) { + return data; + } + QFile f(path); + if (f.open(QIODevice::ReadOnly)) { + QMimeDatabase db; + QMimeType mime = db.mimeTypeForFile(f.fileName()); + data += QL1S("data:"); + data += mime.isValid() ? mime.name() : "application/octet-stream"; + data += QL1S(";base64,"); + data += f.readAll().toBase64(); + } + return data; + +} + +void WebEnginePartErrorSchemeHandler::requestStarted(QWebEngineUrlRequestJob* job) +{ + QBuffer *buf = new QBuffer; + buf->open(QBuffer::ReadWrite); + connect(buf, &QIODevice::aboutToClose, buf, &QObject::deleteLater); + ErrorInfo ei = parseErrorUrl(job->requestUrl()); + writeErrorPage(buf, ei); + buf->seek(0); + job->reply("text/html", buf); +} + +WebEnginePartErrorSchemeHandler::ErrorInfo WebEnginePartErrorSchemeHandler::parseErrorUrl(const QUrl& url) +{ + ErrorInfo ei; + ei.requestUrl = QUrl(url.fragment()); + if (ei.requestUrl.isValid()) { + QString query = url.query(QUrl::FullyDecoded); + QRegularExpression pattern("error=(\\d+)&errText=(.*)"); + QRegularExpressionMatch match = pattern.match(query); + int error = match.captured(1).toInt(); + // error=0 isn't a valid error code, so 0 means it's missing from the URL + if (error == 0) { + error = KIO::ERR_UNKNOWN; + } + ei.text = match.captured(2); + ei.code = error; + } + return ei; +} + +void WebEnginePartErrorSchemeHandler::writeErrorPage(QBuffer* buf, const WebEnginePartErrorSchemeHandler::ErrorInfo& info) +{ + QString errorName, techName, description; + QStringList causes, solutions; + + QByteArray raw = KIO::rawErrorDetail(info.code, info.text, &info.requestUrl); + QDataStream stream(raw); + + stream >> errorName >> techName >> description >> causes >> solutions; + + QFile file(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QL1S("webenginepart/error.html"))); + if (!file.open(QIODevice::ReadOnly)) { + buf->write(i18n("

Unable to display error message

" + "

The error template file error.html could not be " + "found.

").toUtf8()); + return; + } + + QString html(QL1S(file.readAll())); + + QString doc(QL1S("

")); + doc += i18n("The requested operation could not be completed"); + doc += QL1S("

"); + doc += errorName; + doc += QL1S("

"); + + if (!techName.isEmpty()) { + doc += QL1S("

"); + doc += i18n("Technical Reason: %1", techName); + doc += QL1S("

"); + } + + doc += QL1S("

"); + doc += i18n("Details of the Request:"); + doc += QL1S("

  • "); + // escape URL twice: once for i18n, and once for HTML. + doc += i18n("URL: %1", Qt::escape(Qt::escape(info.requestUrl.toDisplayString()))); + doc += QL1S("
  • "); + + const QString protocol(info.requestUrl.scheme()); + if (!protocol.isEmpty()) { + // escape protocol twice: once for i18n, and once for HTML. + doc += i18n("Protocol: %1", Qt::escape(Qt::escape(protocol))); + doc += QL1S("
  • "); + } + + doc += i18n("Date and Time: %1", + QLocale().toString(QDateTime::currentDateTime(), QLocale::LongFormat)); + doc += QL1S("
  • "); + // escape info.text twice: once for i18n, and once for HTML. + doc += i18n("Additional Information: %1", Qt::escape(Qt::escape(info.text))); + doc += QL1S("

"); + doc += i18n("Description:"); + doc += QL1S("

"); + doc += description; + doc += QL1S("

"); + + if (!causes.isEmpty()) { + doc += QL1S("

"); + doc += i18n("Possible Causes:"); + doc += QL1S("

  • "); + doc += causes.join("
  • "); + doc += QL1S("
"); + } + + if (!solutions.isEmpty()) { + doc += QL1S("

"); + doc += i18n("Possible Solutions:"); + doc += QL1S("

  • "); + doc += solutions.join("
  • "); + doc += QL1S("
"); + } + + QString title(i18n("Error: %1", errorName)); + QString direction(QApplication::isRightToLeft() ? "rtl" : "ltr"); + buf->write(html.arg(title, direction, m_warningIconData, doc).toUtf8()); +} +