Messagelib

webenginepage.cpp
1/*
2 SPDX-FileCopyrightText: 2016-2024 Laurent Montel <montel@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "webenginepage.h"
8#include "webenginemanagescript.h"
9#include "webhittest.h"
10
11#include <KLocalizedString>
12
13#include <KColorScheme>
14#include <QEventLoop>
15#include <QFileDialog>
16#include <QGuiApplication>
17#include <QPointer>
18#include <QPrinter>
19#include <QTimer>
20#include <QWebEngineDownloadRequest>
21#include <QWebEngineProfile>
22#include <QWebEngineScriptCollection>
23#include <QWebEngineView>
24
25using namespace WebEngineViewer;
26
27class WebEnginePage::Private
28{
29public:
30 std::optional<QPalette> lastPalette;
31 std::optional<KColorScheme> lastColorScheme;
32 QWebEngineScript refreshScript;
33};
34
36 : QWebEnginePage(new QWebEngineProfile, parent)
37 , d(std::make_unique<WebEnginePage::Private>())
38{
39 // Create a private (off the record) QWebEngineProfile here to isolate the
40 // browsing settings, and adopt it as a child so that it will be deleted
41 // when we are destroyed. The profile must remain active for as long as
42 // any QWebEnginePage's belonging to it exist, see the API documentation
43 // of QWebEnginePage::QWebEnginePage(QWebEngineProfile *, QObject *).
44 // Deleting it as our child on destruction is safe.
45 //
46 // Do not try to save a line of code by setting the parent on construction:
47 //
48 // WebEnginePage::WebEnginePage(QObject *parent)
49 // : QWebEnginePage(new QWebEngineProfile(this), parent)
50 //
51 // because the QWebEngineProfile constructor will call out to the QWebEnginePage
52 // and crash because the QWebEnginePage is not fully constructed yet.
53 profile()->setParent(this);
54
55 init();
56}
57
58WebEnginePage::WebEnginePage(QWebEngineProfile *profile, QObject *parent)
59 : QWebEnginePage(profile, parent)
60 , d(std::make_unique<WebEnginePage::Private>())
61{
62 init();
63}
64
66
67void WebEnginePage::init()
68{
69 connect(profile(), &QWebEngineProfile::downloadRequested, this, &WebEnginePage::saveHtml);
70 qGuiApp->installEventFilter(this);
71
72 // Expose main colors from KColorScheme
73 d->refreshScript.setName(QStringLiteral("injectColorScheme"));
74 d->refreshScript.setSourceCode(refreshCssVariablesScript());
75 d->refreshScript.setInjectionPoint(QWebEngineScript::DocumentReady);
76 d->refreshScript.setRunsOnSubFrames(true);
77 d->refreshScript.setWorldId(QWebEngineScript::ApplicationWorld);
78 scripts().insert(d->refreshScript);
79}
80
81bool WebEnginePage::eventFilter(QObject *obj, QEvent *event)
82{
83 if (obj != qGuiApp) {
84 return false;
85 }
86
88 // Refresh main colors from KColorScheme
89 auto cssVariablesScript = refreshCssVariablesScript();
90 runJavaScript(cssVariablesScript, WebEngineViewer::WebEngineManageScript::scriptWordId());
91
92 d->refreshScript.setSourceCode(cssVariablesScript);
93 scripts().remove(d->refreshScript);
94 scripts().insert(d->refreshScript);
95 }
96 return QObject::eventFilter(obj, event);
97}
98
99QString WebEnginePage::refreshCssVariablesScript()
100{
101 // Same logic as KIconColors
102 auto palette = qGuiApp->palette();
103 if (!d->lastColorScheme || !d->lastPalette || palette != d->lastPalette) {
104 d->lastPalette = palette;
105 d->lastColorScheme = KColorScheme(QPalette::Active, KColorScheme::View);
106 }
107
108 const auto script = QStringLiteral(R"RAW(
109{
110 const root = document.querySelector(':root');
111 root.style.setProperty('--kcolorscheme-background', '%1');
112 root.style.setProperty('--kcolorscheme-text', '%2');
113 root.style.setProperty('--kcolorscheme-highlight', '%3');
114 root.style.setProperty('--kcolorscheme-highlight-text', '%4');
115 root.style.setProperty('--kcolorscheme-accent', '%5');
116 root.style.setProperty('--kcolorscheme-positive-text', '%6');
117 root.style.setProperty('--kcolorscheme-positive-background', '%7');
118 root.style.setProperty('--kcolorscheme-neutral-text', '%8');
119 root.style.setProperty('--kcolorscheme-neutral-background', '%9');
120 root.style.setProperty('--kcolorscheme-negative-text', '%10');
121 root.style.setProperty('--kcolorscheme-negative-background', '%11');
122 root.style.setProperty('--kcolorscheme-active-text', '%12');
123 root.style.setProperty('--kcolorscheme-active-background', '%13');
124 root.style.setProperty('--kcolorscheme-text-disabled', '%14');
125 root.style.setProperty('--kcolorscheme-text-link', '%15');
126}
127 )RAW")
128 .arg(d->lastColorScheme->background(KColorScheme::NormalBackground).color().name(),
129 d->lastColorScheme->foreground(KColorScheme::NormalText).color().name(),
130 palette.highlight().color().name(),
131 palette.highlightedText().color().name(),
132 palette.accent().color().name(),
133 d->lastColorScheme->foreground(KColorScheme::PositiveText).color().name(),
134 d->lastColorScheme->background(KColorScheme::PositiveBackground).color().name(),
135 d->lastColorScheme->foreground(KColorScheme::NeutralText).color().name(),
136 d->lastColorScheme->background(KColorScheme::NeutralBackground).color().name(),
137 d->lastColorScheme->foreground(KColorScheme::NegativeText).color().name(),
138 d->lastColorScheme->background(KColorScheme::NegativeBackground).color().name(),
139 d->lastColorScheme->foreground(KColorScheme::ActiveText).color().name(),
140 d->lastColorScheme->background(KColorScheme::ActiveBackground).color().name(),
141 d->lastColorScheme->foreground(KColorScheme::InactiveText).color().name(),
142 d->lastColorScheme->foreground(KColorScheme::LinkText).color().name());
143
144 return script;
145}
146
147WebEngineViewer::WebHitTest *WebEnginePage::hitTestContent(const QPoint &pos)
148{
149 return new WebHitTest(this, mapToViewport(pos), pos);
150}
151
152QPoint WebEnginePage::mapToViewport(const QPoint &pos) const
153{
154 return QPoint(pos.x() / zoomFactor(), pos.y() / zoomFactor());
155}
156
157void WebEnginePage::saveHtml(QWebEngineDownloadRequest *download)
158{
159 const QString fileName = QFileDialog::getSaveFileName(QWebEngineView::forPage(this), i18n("Save HTML Page"));
160 if (!fileName.isEmpty()) {
161 download->setSavePageFormat(QWebEngineDownloadRequest::SingleHtmlSaveFormat);
162 download->setDownloadDirectory(QFileInfo(fileName).path());
163 download->setDownloadFileName(QFileInfo(fileName).fileName());
164 download->accept();
165 }
166}
167
168bool WebEnginePage::acceptNavigationRequest(const QUrl &url, NavigationType type, bool isMainFrame)
169{
170 if (isMainFrame && type == NavigationTypeLinkClicked) {
171 Q_EMIT urlClicked(url);
172 return false;
173 }
174 return true;
175}
176
177void WebEnginePage::javaScriptConsoleMessage(QWebEnginePage::JavaScriptConsoleMessageLevel level,
178 const QString &message,
179 int lineNumber,
180 const QString &sourceID)
181{
182 Q_UNUSED(level)
183 Q_UNUSED(sourceID)
184 // Don't convert to debug categories
185 qDebug() << "WebEnginePage::javaScriptConsoleMessage lineNumber: " << lineNumber << " message: " << message;
186 Q_EMIT showConsoleMessage(message);
187}
188
189#include "moc_webenginepage.cpp"
The WebEnginePage class.
~WebEnginePage() override
Destructor.
WebEnginePage(QObject *parent=nullptr)
Constructor.
The WebHitTest class.
Definition webhittest.h:23
QString i18n(const char *text, const TYPE &arg...)
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
QString path(const QString &relativePath)
ApplicationPaletteChange
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, Options options)
virtual bool eventFilter(QObject *watched, QEvent *event)
int x() const const
int y() const const
bool isEmpty() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jul 26 2024 11:54:19 by doxygen 1.11.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.