diff --git a/applets/notifications/package/contents/ui/NotificationItem.qml b/applets/notifications/package/contents/ui/NotificationItem.qml --- a/applets/notifications/package/contents/ui/NotificationItem.qml +++ b/applets/notifications/package/contents/ui/NotificationItem.qml @@ -27,6 +27,8 @@ import org.kde.plasma.extras 2.0 as PlasmaExtras import org.kde.kquickcontrolsaddons 2.0 +import org.kde.plasma.private.notifications 1.0 as Notifications + MouseArea { id: notificationItem width: parent.width @@ -45,14 +47,18 @@ property alias icon: appIconItem.source property alias image: imageItem.image property alias summary: summaryLabel.text - property alias body: bodyText.text + property string body property alias configurable: settingsButton.visible property var created property int maximumTextHeight: -1 property ListModel actions: ListModel { } + onBodyChanged: { + Notifications.TextSanitizer.setText(bodyText, body); + } + function pressedAction() { for (var i = 0, count = actionRepeater.count; i < count; ++i) { var item = actionRepeater.itemAt(i) diff --git a/applets/notifications/plugin/CMakeLists.txt b/applets/notifications/plugin/CMakeLists.txt --- a/applets/notifications/plugin/CMakeLists.txt +++ b/applets/notifications/plugin/CMakeLists.txt @@ -1,6 +1,7 @@ set(notificationshelper_SRCS notificationshelper.cpp notificationshelperplugin.cpp + textsanitizer.cpp ) add_library(notificationshelperplugin SHARED ${notificationshelper_SRCS}) diff --git a/applets/notifications/plugin/notificationshelperplugin.h b/applets/notifications/plugin/notificationshelperplugin.h --- a/applets/notifications/plugin/notificationshelperplugin.h +++ b/applets/notifications/plugin/notificationshelperplugin.h @@ -30,7 +30,6 @@ public: void registerTypes(const char *uri) override; - void initializeEngine(QQmlEngine *engine, const char *uri) override; }; diff --git a/applets/notifications/plugin/notificationshelperplugin.cpp b/applets/notifications/plugin/notificationshelperplugin.cpp --- a/applets/notifications/plugin/notificationshelperplugin.cpp +++ b/applets/notifications/plugin/notificationshelperplugin.cpp @@ -18,22 +18,12 @@ #include "notificationshelperplugin.h" #include "notificationshelper.h" +#include "textsanitizer.h" #include #include -#include #include -class NoAccessNetworkAccessManagerFactory : public QQmlNetworkAccessManagerFactory -{ -public: - QNetworkAccessManager *create(QObject *parent) override { - QNetworkAccessManager *manager = new QNetworkAccessManager(parent); - manager->setNetworkAccessible(QNetworkAccessManager::NotAccessible); - return manager; - } -}; - class UrlHelper : public QObject { Q_OBJECT public: @@ -67,23 +57,22 @@ return new ProcessRunner(); } -void NotificationsHelperPlugin::registerTypes(const char *uri) +static QObject *textsanitizer_singleton_provider(QQmlEngine *engine, QJSEngine *scriptEngine) { - Q_ASSERT(uri == QLatin1String("org.kde.plasma.private.notifications")); + Q_UNUSED(engine) + Q_UNUSED(scriptEngine) - qmlRegisterType(uri, 1, 0, "NotificationsHelper"); - qmlRegisterSingletonType(uri, 1, 0, "UrlHelper", urlcheck_singletontype_provider); - qmlRegisterSingletonType(uri, 1, 0, "ProcessRunner", processrunner_singleton_provider); + return new TextSanitizer(); } -void NotificationsHelperPlugin::initializeEngine(QQmlEngine *engine, const char *uri) +void NotificationsHelperPlugin::registerTypes(const char *uri) { Q_ASSERT(uri == QLatin1String("org.kde.plasma.private.notifications")); - auto oldFactory = engine->networkAccessManagerFactory(); - engine->setNetworkAccessManagerFactory(nullptr); - delete oldFactory; - engine->setNetworkAccessManagerFactory(new NoAccessNetworkAccessManagerFactory); + qmlRegisterType(uri, 1, 0, "NotificationsHelper"); + qmlRegisterSingletonType(uri, 1, 0, "UrlHelper", urlcheck_singletontype_provider); + qmlRegisterSingletonType(uri, 1, 0, "ProcessRunner", processrunner_singleton_provider); + qmlRegisterSingletonType(uri, 1, 0, "TextSanitizer", textsanitizer_singleton_provider); } #include "notificationshelperplugin.moc" diff --git a/applets/notifications/plugin/textsanitizer.h b/applets/notifications/plugin/textsanitizer.h new file mode 100644 --- /dev/null +++ b/applets/notifications/plugin/textsanitizer.h @@ -0,0 +1,36 @@ +/* + Copyright (C) 2017 Kai Uwe Broulik + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#pragma once + +#include + +class QQuickItem; +class QString; + +class TextSanitizer : public QObject +{ + Q_OBJECT + +public: + explicit TextSanitizer(QObject *parent = nullptr); + ~TextSanitizer() override; + + Q_INVOKABLE void setText(QQuickItem *target, const QString &text) const; + +}; diff --git a/applets/notifications/plugin/textsanitizer.cpp b/applets/notifications/plugin/textsanitizer.cpp new file mode 100644 --- /dev/null +++ b/applets/notifications/plugin/textsanitizer.cpp @@ -0,0 +1,91 @@ +/* + Copyright (C) 2017 Kai Uwe Broulik + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "textsanitizer.h" + +#include +#include +#include +#include +#include + +TextSanitizer::TextSanitizer(QObject *parent) : QObject(parent) +{ + +} + +TextSanitizer::~TextSanitizer() = default; + +void TextSanitizer::setText(QQuickItem *target, const QString &text) const +{ + QQuickTextDocument *qqdoc = target->property("textDocument").value(); + if (!qqdoc) { + return; + } + + QScopedPointer newDocument(new QTextDocument()); + newDocument->setHtml(text); + + for (QTextBlock block = newDocument->begin(); block != newDocument->end(); block = block.next()) { + auto it = block.begin(); + while (!it.atEnd()) { + QTextFragment fragment = it.fragment(); + ++it; + + if (!fragment.isValid()) { + continue; + } + + QTextFormat format = fragment.charFormat(); + + // Remove any background-image that points to a non-local location. + // QtQuick doesn't render or even seem to load them but if we're going through + // all of this trouble already we might as well look at everything. + QUrl backgroundUrl = format.property(QTextFormat::BackgroundImageUrl).toUrl(); + if (!backgroundUrl.isLocalFile()) { + format.setProperty(QTextFormat::BackgroundImageUrl, QVariant()); + } + + QTextImageFormat imageFormat = format.toImageFormat(); + if (!imageFormat.isValid()) { + continue; + } + + // Remove any whose src points to a non-local location. + QUrl imageUrl(imageFormat.name()); + if (!imageUrl.isValid() || !imageUrl.isLocalFile()) { + // Now get rid of that entire thing. + QTextCursor cursor(newDocument.data()); + cursor.setPosition(fragment.position()); + cursor.setPosition(fragment.position() + fragment.length(), QTextCursor::KeepAnchor); + cursor.removeSelectedText(); + it = block.begin(); // start all over FIXME can we use some "mutable iterator" thing? + } + + // The third place where external resources may be loaded by Qt is for + // but QQuickTextDocumentWithImageResources only handles images, no stylesheets, so we + // should be safe here. Also, the stylseheets are resolved independent of the TextDocument fragments, + // so we cannot easily intercept this here. + } + + // FIXME is there a better way? + // we cannot just setHtml on the TextEdit's textDocument at the beginning + // or it starts loading images right away + qqdoc->textDocument()->setHtml(newDocument->toHtml()); + } +}