Falkon Develop
Cross-platform Qt-based web browser
falkonschemehandler.cpp
Go to the documentation of this file.
1/* ============================================================
2* Falkon - Qt web browser
3* Copyright (C) 2010-2018 David Rosca <nowrep@gmail.com>
4*
5* This program is free software: you can redistribute it and/or modify
6* it under the terms of the GNU General Public License as published by
7* the Free Software Foundation, either version 3 of the License, or
8* (at your option) any later version.
9*
10* This program is distributed in the hope that it will be useful,
11* but WITHOUT ANY WARRANTY; without even the implied warranty of
12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13* GNU General Public License for more details.
14*
15* You should have received a copy of the GNU General Public License
16* along with this program. If not, see <http://www.gnu.org/licenses/>.
17* ============================================================ */
18#include "falkonschemehandler.h"
19#include "qztools.h"
20#include "browserwindow.h"
21#include "mainapplication.h"
22#include "speeddial.h"
23#include "pluginproxy.h"
24#include "plugininterface.h"
25#include "settings.h"
26#include "datapaths.h"
27#include "iconprovider.h"
28#include "sessionmanager.h"
29#include "restoremanager.h"
30#include "../config.h"
31
32#include <QTimer>
33#include <QSettings>
34#include <QUrlQuery>
35#include <QWebEngineProfile>
36#include <QWebEngineUrlRequestJob>
37#include <QtWebEngineCoreVersion>
38#include <QMetaType>
39
40static QString authorString(const char* name, const QString &mail)
41{
42 return QSL("%1 &lt;<a href=\"mailto:%2\">%2</a>&gt;").arg(QString::fromUtf8(name), mail);
43}
44
46 : QWebEngineUrlSchemeHandler(parent)
47{
48}
49
50void FalkonSchemeHandler::requestStarted(QWebEngineUrlRequestJob *job)
51{
52 if (handleRequest(job)) {
53 return;
54 }
55
56 QStringList knownPages;
57 knownPages << QSL("about") << QSL("start") << QSL("speeddial") << QSL("config") << QSL("restore");
58
59 if (knownPages.contains(job->requestUrl().path()))
60 job->reply(QByteArrayLiteral("text/html"), new FalkonSchemeReply(job, job));
61 else
62 job->fail(QWebEngineUrlRequestJob::UrlInvalid);
63}
64
65bool FalkonSchemeHandler::handleRequest(QWebEngineUrlRequestJob *job)
66{
67 QUrlQuery query(job->requestUrl());
68 if (!query.isEmpty() && job->requestUrl().path() == QL1S("restore")) {
69 if (mApp->restoreManager()) {
70 if (query.hasQueryItem(QSL("new-session"))) {
71 mApp->restoreManager()->clearRestoreData();
72 } else if (query.hasQueryItem(QSL("restore-session"))) {
73 mApp->restoreSession(nullptr, mApp->restoreManager()->restoreData());
74 }
75 }
76 mApp->destroyRestoreManager();
77 job->redirect(QUrl(QSL("falkon:start")));
78 return true;
79 } else if (job->requestUrl().path() == QL1S("reportbug")) {
80 job->redirect(QUrl(QString::fromLatin1(Qz::BUGSADDRESS)));
81 return true;
82 }
83
84 return false;
85}
86
87FalkonSchemeReply::FalkonSchemeReply(QWebEngineUrlRequestJob *job, QObject *parent)
88 : QIODevice(parent)
89 , m_loaded(false)
90 , m_job(job)
91{
92 m_pageName = m_job->requestUrl().path();
93 loadPage();
94}
95
96void FalkonSchemeReply::loadPage()
97{
98 if (m_loaded)
99 return;
100
101 QString contents;
102
103 if (m_pageName == QLatin1String("about")) {
104 contents = aboutPage();
105 } else if (m_pageName == QLatin1String("start")) {
106 contents = startPage();
107 } else if (m_pageName == QLatin1String("speeddial")) {
108 contents = speeddialPage();
109 } else if (m_pageName == QLatin1String("config")) {
110 contents = configPage();
111 } else if (m_pageName == QLatin1String("restore")) {
112 contents = restorePage();
113 }
114
115 QMutexLocker lock(&m_mutex);
116 m_buffer.setData(contents.toUtf8());
117 m_buffer.open(QIODevice::ReadOnly);
118 lock.unlock();
119
120 Q_EMIT readyRead();
121
122 m_loaded = true;
123}
124
126{
127 QMutexLocker lock(&m_mutex);
128 return m_buffer.bytesAvailable();
129}
130
131qint64 FalkonSchemeReply::readData(char *data, qint64 maxSize)
132{
133 QMutexLocker lock(&m_mutex);
134 return m_buffer.read(data, maxSize);
135}
136
137qint64 FalkonSchemeReply::writeData(const char *data, qint64 len)
138{
139 Q_UNUSED(data);
140 Q_UNUSED(len);
141
142 return 0;
143}
144
145QString FalkonSchemeReply::startPage()
146{
147 static QString sPage;
148
149 if (!sPage.isEmpty()) {
150 return sPage;
151 }
152
153 sPage.append(QzTools::readAllFileContents(QSL(":html/start.html")));
154 sPage.replace(QLatin1String("%ABOUT-IMG%"), QSL("qrc:icons/other/startpage.svg"));
155 sPage.replace(QLatin1String("%ABOUT-IMG-DARK%"), QSL("qrc:icons/other/startpage-dark.svg"));
156
157 sPage.replace(QLatin1String("%TITLE%"), tr("Start Page"));
158 sPage.replace(QLatin1String("%BUTTON-LABEL%"), tr("Search on Web"));
159 sPage.replace(QLatin1String("%SEARCH-BY%"), tr("Search results provided by DuckDuckGo"));
160 sPage.replace(QLatin1String("%WWW%"), QString::fromLatin1(Qz::WIKIADDRESS));
161 sPage.replace(QLatin1String("%ABOUT-FALKON%"), tr("About Falkon"));
162 sPage.replace(QLatin1String("%PRIVATE-BROWSING%"), mApp->isPrivate() ? tr("<h1>Private Browsing</h1>") : QString());
163 sPage = QzTools::applyDirectionToPage(sPage);
164
165 return sPage;
166}
167
168QString FalkonSchemeReply::aboutPage()
169{
170 static QString aPage;
171
172 if (aPage.isEmpty()) {
173 aPage.append(QzTools::readAllFileContents(QSL(":html/about.html")));
174 aPage.replace(QLatin1String("%ABOUT-IMG%"), QSL("qrc:icons/other/about.svg"));
175 aPage.replace(QLatin1String("%ABOUT-IMG-DARK%"), QSL("qrc:icons/other/about-dark.svg"));
176 aPage.replace(QLatin1String("%COPYRIGHT-INCLUDE%"), QzTools::readAllFileContents(QSL(":html/copyright")).toHtmlEscaped());
177
178 aPage.replace(QLatin1String("%TITLE%"), tr("About Falkon"));
179 aPage.replace(QLatin1String("%ABOUT-FALKON%"), tr("About Falkon"));
180 aPage.replace(QLatin1String("%INFORMATIONS-ABOUT-VERSION%"), tr("Information about version"));
181 aPage.replace(QLatin1String("%COPYRIGHT%"), tr("Copyright"));
182
183 aPage.replace(QLatin1String("%VERSION-INFO%"),
184 QSL("<dt>%1</dt><dd>%2<dd>").arg(tr("Version"),
185#ifdef FALKON_GIT_REVISION
186 QSL("%1 (%2)").arg(QString::fromLatin1(Qz::VERSION), QL1S(FALKON_GIT_REVISION))));
187#else
188 QString::fromLatin1(Qz::VERSION)));
189#endif
190
191 aPage.replace(QLatin1String("%MAIN-DEVELOPER%"), tr("Main developer"));
192 aPage.replace(QLatin1String("%MAIN-DEVELOPER-TEXT%"), authorString(Qz::AUTHOR, QSL("nowrep@gmail.com")));
193 aPage = QzTools::applyDirectionToPage(aPage);
194 }
195
196 return aPage;
197}
198
199QString FalkonSchemeReply::speeddialPage()
200{
201 static QString dPage;
202
203 if (dPage.isEmpty()) {
204 dPage.append(QzTools::readAllFileContents(QSL(":html/speeddial.html")));
205 dPage.replace(QLatin1String("%IMG_PLUS%"), QLatin1String("qrc:html/plus.svg"));
206 dPage.replace(QLatin1String("%IMG_CLOSE%"), QLatin1String("qrc:html/close.svg"));
207 dPage.replace(QLatin1String("%IMG_EDIT%"), QLatin1String("qrc:html/edit.svg"));
208 dPage.replace(QLatin1String("%IMG_RELOAD%"), QLatin1String("qrc:html/reload.svg"));
209 dPage.replace(QLatin1String("%LOADING-IMG%"), QLatin1String("qrc:html/loading.gif"));
210 dPage.replace(QLatin1String("%IMG_SETTINGS%"), QLatin1String("qrc:html/configure.svg"));
211
212 dPage.replace(QLatin1String("%SITE-TITLE%"), tr("Speed Dial"));
213 dPage.replace(QLatin1String("%ADD-TITLE%"), tr("Add New Page"));
214 dPage.replace(QLatin1String("%TITLE-EDIT%"), tr("Edit"));
215 dPage.replace(QLatin1String("%TITLE-REMOVE%"), tr("Remove"));
216 dPage.replace(QLatin1String("%TITLE-RELOAD%"), tr("Reload"));
217 dPage.replace(QLatin1String("%TITLE-WARN%"), tr("Are you sure you want to remove this speed dial?"));
218 dPage.replace(QLatin1String("%TITLE-WARN-REL%"), tr("Are you sure you want to reload all speed dials?"));
219 dPage.replace(QLatin1String("%TITLE-FETCHTITLE%"), tr("Load title from page"));
220 dPage.replace(QLatin1String("%JAVASCRIPT-DISABLED%"), tr("SpeedDial requires enabled JavaScript."));
221 dPage.replace(QLatin1String("%URL%"), tr("Url"));
222 dPage.replace(QLatin1String("%TITLE%"), tr("Title"));
223 dPage.replace(QLatin1String("%APPLY%"), tr("Apply"));
224 dPage.replace(QLatin1String("%CANCEL%"), tr("Cancel"));
225 dPage.replace(QLatin1String("%NEW-PAGE%"), tr("New Page"));
226 dPage.replace(QLatin1String("%SETTINGS-TITLE%"), tr("Speed Dial settings"));
227 dPage.replace(QLatin1String("%TXT_PLACEMENT%"), tr("Placement: "));
228 dPage.replace(QLatin1String("%TXT_AUTO%"), tr("Auto"));
229 dPage.replace(QLatin1String("%TXT_COVER%"), tr("Cover"));
230 dPage.replace(QLatin1String("%TXT_FIT%"), tr("Fit"));
231 dPage.replace(QLatin1String("%TXT_FWIDTH%"), tr("Fit Width"));
232 dPage.replace(QLatin1String("%TXT_FHEIGHT%"), tr("Fit Height"));
233 dPage.replace(QLatin1String("%TXT_NOTE%"), tr("Use custom wallpaper"));
234 dPage.replace(QLatin1String("%TXT_SELECTIMAGE%"), tr("Click to select image"));
235 dPage.replace(QLatin1String("%TXT_NRROWS%"), tr("Maximum pages in a row:"));
236 dPage.replace(QLatin1String("%TXT_SDSIZE%"), tr("Change size of pages:"));
237 dPage.replace(QLatin1String("%TXT_CNTRDLS%"), tr("Center speed dials"));
238 dPage.replace(QLatin1String("%TXT_LOCKDIALS%"), tr("Lock the position of SpeedDial entries."));
239 dPage = QzTools::applyDirectionToPage(dPage);
240 }
241
242 QString page = dPage;
243 SpeedDial* dial = mApp->plugins()->speedDial();
244
245 page.replace(QLatin1String("%INITIAL-SCRIPT%"), QString::fromLatin1(dial->initialScript().toUtf8().toBase64()));
246 page.replace(QLatin1String("%IMG_BACKGROUND%"), dial->backgroundImage());
247 page.replace(QLatin1String("%URL_BACKGROUND%"), dial->backgroundImageUrl());
248 page.replace(QLatin1String("%B_SIZE%"), dial->backgroundImageSize());
249 page.replace(QLatin1String("%ROW-PAGES%"), QString::number(dial->pagesInRow()));
250 page.replace(QLatin1String("%SD-SIZE%"), QString::number(dial->sdSize()));
251 page.replace(QLatin1String("%SD-CENTER%"), dial->sdCenter() ? QSL("true") : QSL("false"));
252 page.replace(QLatin1String("%LOCK-DIALS%"), dial->lockDials() ? QSL("true") : QSL("false"));
253
254 return page;
255}
256
257QString FalkonSchemeReply::restorePage()
258{
259 static QString rPage;
260
261 if (rPage.isEmpty()) {
262 rPage.append(QzTools::readAllFileContents(QSL(":html/restore.html")));
263 rPage.replace(QLatin1String("%IMAGE%"), QzTools::pixmapToDataUrl(IconProvider::standardIcon(QStyle::SP_MessageBoxWarning).pixmap(45)).toString());
264 rPage.replace(QLatin1String("%TITLE%"), tr("Restore Session"));
265 rPage.replace(QLatin1String("%OOPS%"), tr("Oops, Falkon crashed."));
266 rPage.replace(QLatin1String("%APOLOGIZE%"), tr("We apologize for this. Would you like to restore the last saved state?"));
267 rPage.replace(QLatin1String("%TRY-REMOVING%"), tr("Try removing one or more tabs that you think cause troubles"));
268 rPage.replace(QLatin1String("%START-NEW%"), tr("Or you can start completely new session"));
269 rPage.replace(QLatin1String("%WINDOW%"), tr("Window"));
270 rPage.replace(QLatin1String("%WINDOWS-AND-TABS%"), tr("Windows and Tabs"));
271 rPage.replace(QLatin1String("%BUTTON-START-NEW%"), tr("Start New Session"));
272 rPage.replace(QLatin1String("%BUTTON-RESTORE%"), tr("Restore"));
273 rPage.replace(QLatin1String("%JAVASCRIPT-DISABLED%"), tr("Requires enabled JavaScript."));
274 rPage = QzTools::applyDirectionToPage(rPage);
275 }
276
277 return rPage;
278}
279
280QString FalkonSchemeReply::configPage()
281{
282 static QString cPage;
283
284 if (cPage.isEmpty()) {
285 cPage.append(QzTools::readAllFileContents(QSL(":html/config.html")));
286 cPage.replace(QLatin1String("%ABOUT-IMG%"), QSL("qrc:icons/other/about.svg"));
287 cPage.replace(QLatin1String("%ABOUT-IMG-DARK%"), QSL("qrc:icons/other/about-dark.svg"));
288
289 cPage.replace(QLatin1String("%TITLE%"), tr("Configuration Information"));
290 cPage.replace(QLatin1String("%CONFIG%"), tr("Configuration Information"));
291 cPage.replace(QLatin1String("%INFORMATIONS-ABOUT-VERSION%"), tr("Information about version"));
292 cPage.replace(QLatin1String("%CONFIG-ABOUT%"), tr("This page contains information about Falkon's current configuration - relevant for troubleshooting. Please include this information when submitting bug reports."));
293 cPage.replace(QLatin1String("%BROWSER-IDENTIFICATION%"), tr("Browser Identification"));
294 cPage.replace(QLatin1String("%PATHS%"), tr("Paths"));
295 cPage.replace(QLatin1String("%BUILD-CONFIG%"), tr("Build Configuration"));
296 cPage.replace(QLatin1String("%PREFS%"), tr("Preferences"));
297 cPage.replace(QLatin1String("%OPTION%"), tr("Option"));
298 cPage.replace(QLatin1String("%VALUE%"), tr("Value"));
299 cPage.replace(QLatin1String("%PLUGINS%"), tr("Extensions"));
300 cPage.replace(QLatin1String("%PL-NAME%"), tr("Name"));
301 cPage.replace(QLatin1String("%PL-VER%"), tr("Version"));
302 cPage.replace(QLatin1String("%PL-AUTH%"), tr("Author"));
303 cPage.replace(QLatin1String("%PL-DESC%"), tr("Description"));
304
305 auto allPaths = [](DataPaths::Path type) {
306 QString out;
307 const auto paths = DataPaths::allPaths(type);
308 for (const QString &path : paths) {
309 if (!out.isEmpty()) {
310 out.append(QSL("<br>"));
311 }
312 out.append(path);
313 }
314 return out;
315 };
316
317 cPage.replace(QLatin1String("%VERSION-INFO%"),
318 QSL("<dt>%1</dt><dd>%2<dd>").arg(tr("Application version"),
319#ifdef FALKON_GIT_REVISION
320 QSL("%1 (%2)").arg(QString::fromLatin1(Qz::VERSION), QL1S(FALKON_GIT_REVISION))
321#else
322 QString::fromLatin1(Qz::VERSION)
323#endif
324 ) +
325 QSL("<dt>%1</dt><dd>%2<dd>").arg(tr("Qt version"), QString::fromLatin1(qVersion())) +
326 QSL("<dt>%1</dt><dd>%2<dd>").arg(tr("QtWebEngine version"), QSL(QTWEBENGINECORE_VERSION_STR)) +
327 QSL("<dt>%1</dt><dd>%2<dd>").arg(tr("Platform"), QzTools::operatingSystemLong()));
328
329 cPage.replace(QLatin1String("%PATHS-TEXT%"),
330 QSL("<dt>%1</dt><dd>%2<dd>").arg(tr("Profile"), DataPaths::currentProfilePath()) +
331 QSL("<dt>%1</dt><dd>%2<dd>").arg(tr("Settings"), DataPaths::currentProfilePath() + QSL("/settings.ini")) +
332 QSL("<dt>%1</dt><dd>%2<dd>").arg(tr("Saved session"), SessionManager::defaultSessionPath()) +
333 QSL("<dt>%1</dt><dd>%2<dd>").arg(tr("Data"), allPaths(DataPaths::AppData)) +
334 QSL("<dt>%1</dt><dd>%2<dd>").arg(tr("Themes"), allPaths(DataPaths::Themes)) +
335 QSL("<dt>%1</dt><dd>%2<dd>").arg(tr("Extensions"), allPaths(DataPaths::Plugins)));
336
337#ifdef QT_DEBUG
338 QString debugBuild = tr("<b>Enabled</b>");
339#else
340 QString debugBuild = tr("Disabled");
341#endif
342
343#ifdef Q_OS_WIN
344#if defined(Q_OS_WIN) && defined(W7API)
345 QString w7APIEnabled = tr("<b>Enabled</b>");
346#else
347 QString w7APIEnabled = tr("Disabled");
348#endif
349#endif
350
351 QString portableBuild = mApp->isPortable() ? tr("<b>Enabled</b>") : tr("Disabled");
352
353 cPage.replace(QLatin1String("%BUILD-CONFIG-TEXT%"),
354 QSL("<dt>%1</dt><dd>%2<dd>").arg(tr("Debug build"), debugBuild) +
355#ifdef Q_OS_WIN
356 QSL("<dt>%1</dt><dd>%2<dd>").arg(tr("Windows 7 API"), w7APIEnabled) +
357#endif
358 QSL("<dt>%1</dt><dd>%2<dd>").arg(tr("Portable build"), portableBuild));
359
360 cPage = QzTools::applyDirectionToPage(cPage);
361 }
362
363 QString page = cPage;
364 page.replace(QLatin1String("%USER-AGENT%"), mApp->webProfile()->httpUserAgent());
365
366 QString pluginsString;
367 const QList<Plugins::Plugin> &availablePlugins = mApp->plugins()->availablePlugins();
368
369 for (const Plugins::Plugin &plugin : availablePlugins) {
370 PluginSpec spec = plugin.pluginSpec;
371 pluginsString.append(QSL("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td></tr>").arg(
372 spec.name, spec.version, spec.author.toHtmlEscaped(), spec.description));
373 }
374
375 if (pluginsString.isEmpty()) {
376 pluginsString = QSL("<tr><td colspan=4 class=\"no-available-plugins\">%1</td></tr>").arg(tr("No available extensions."));
377 }
378
379 page.replace(QLatin1String("%PLUGINS-INFO%"), pluginsString);
380
381 QString allGroupsString;
382 QSettings* settings = Settings::globalSettings();
383 const auto groups = settings->childGroups();
384 for (const QString &group : groups) {
385 QString groupString = QSL("<tr><th colspan=\"2\">[%1]</th></tr>").arg(group);
386 settings->beginGroup(group);
387
388 const auto keys = settings->childKeys();
389 for (const QString &key : keys) {
390 const QVariant keyValue = settings->value(key);
391 QString keyString;
392
393 switch (keyValue.typeId()) {
394 case QMetaType::QByteArray:
395 keyString = QLatin1String("QByteArray");
396 break;
397
398 case QMetaType::QPoint: {
399 const QPoint point = keyValue.toPoint();
400 keyString = QSL("QPoint(%1, %2)").arg(point.x()).arg(point.y());
401 break;
402 }
403
404 case QMetaType::QStringList:
405 keyString = keyValue.toStringList().join(QSL(","));
406 break;
407
408 default:
409 keyString = keyValue.toString();
410 }
411
412 if (keyString.isEmpty()) {
413 keyString = QLatin1String("\"empty\"");
414 }
415
416 groupString.append(QSL("<tr><td>%1</td><td>%2</td></tr>").arg(key, keyString.toHtmlEscaped()));
417 }
418
419 settings->endGroup();
420 allGroupsString.append(groupString);
421 }
422
423 page.replace(QLatin1String("%PREFS-INFO%"), allGroupsString);
424
425 return page;
426}
static QStringList allPaths(Path type)
Definition: datapaths.cpp:74
static QString currentProfilePath()
Definition: datapaths.cpp:95
FalkonSchemeHandler(QObject *parent=nullptr)
void requestStarted(QWebEngineUrlRequestJob *job) Q_DECL_OVERRIDE
FalkonSchemeReply(QWebEngineUrlRequestJob *job, QObject *parent=nullptr)
qint64 writeData(const char *data, qint64 len) Q_DECL_OVERRIDE
qint64 bytesAvailable() const Q_DECL_OVERRIDE
qint64 readData(char *data, qint64 maxSize) Q_DECL_OVERRIDE
static QIcon standardIcon(QStyle::StandardPixmap icon)
static QUrl pixmapToDataUrl(const QPixmap &pix)
Definition: qztools.cpp:83
static QString readAllFileContents(const QString &filename)
Definition: qztools.cpp:98
static QString applyDirectionToPage(QString &pageContents)
Definition: qztools.cpp:439
static QString operatingSystemLong()
Definition: qztools.cpp:1015
static QString defaultSessionPath()
static QSettings * globalSettings()
Definition: settings.cpp:94
int sdSize()
Definition: speeddial.cpp:166
QString initialScript()
Definition: speeddial.cpp:206
QString backgroundImageSize()
Definition: speeddial.cpp:199
bool sdCenter()
Definition: speeddial.cpp:173
bool lockDials()
Definition: speeddial.cpp:180
QString backgroundImageUrl()
Definition: speeddial.cpp:194
QString backgroundImage()
Definition: speeddial.cpp:187
int pagesInRow()
Definition: speeddial.cpp:159
#define mApp
FALKON_EXPORT const char * AUTHOR
Definition: qzcommon.cpp:27
FALKON_EXPORT const char * VERSION
Definition: qzcommon.cpp:26
FALKON_EXPORT const char * BUGSADDRESS
Definition: qzcommon.cpp:30
FALKON_EXPORT const char * WIKIADDRESS
Definition: qzcommon.cpp:31
#define QL1S(x)
Definition: qzcommon.h:44
#define QSL(x)
Definition: qzcommon.h:40
QString version
Definition: plugins.h:38
QString name
Definition: plugins.h:35
QString description
Definition: plugins.h:36
QString author
Definition: plugins.h:37