diff options
Diffstat (limited to 'src/webengine')
-rw-r--r-- | src/webengine/meson.build | 27 | ||||
-rw-r--r-- | src/webengine/test/form.html | 10 | ||||
-rw-r--r-- | src/webengine/test/icon.svg | 7 | ||||
-rw-r--r-- | src/webengine/test/profile.cpp | 124 | ||||
-rw-r--r-- | src/webengine/test/profilemanager.cpp | 120 | ||||
-rw-r--r-- | src/webengine/test/sample.html | 7 | ||||
-rw-r--r-- | src/webengine/test/testing.profile | 8 | ||||
-rw-r--r-- | src/webengine/test/view.cpp | 92 | ||||
-rw-r--r-- | src/webengine/urlinterceptor.cpp | 45 | ||||
-rw-r--r-- | src/webengine/urlinterceptor.h | 17 | ||||
-rw-r--r-- | src/webengine/webpage.cpp | 8 | ||||
-rw-r--r-- | src/webengine/webpage.h | 6 | ||||
-rw-r--r-- | src/webengine/webprofile.cpp | 198 | ||||
-rw-r--r-- | src/webengine/webprofile.h | 167 | ||||
-rw-r--r-- | src/webengine/webprofilemanager.cpp | 147 | ||||
-rw-r--r-- | src/webengine/webprofilemanager.h | 102 | ||||
-rw-r--r-- | src/webengine/webview.cpp | 54 | ||||
-rw-r--r-- | src/webengine/webview.h | 20 | ||||
-rw-r--r-- | src/webengine/webviewcontextmenu.cpp | 3 |
19 files changed, 743 insertions, 419 deletions
diff --git a/src/webengine/meson.build b/src/webengine/meson.build new file mode 100644 index 0000000..db7bdc5 --- /dev/null +++ b/src/webengine/meson.build @@ -0,0 +1,27 @@ +webengine_moc = mod_qt5.preprocess( + moc_headers: [ 'webprofile.h', 'webpage.h', 'webview.h' ], + dependencies: dep_qt5 +) + +dep_webengine = declare_dependency( + include_directories: [ '.', smolbote_include ], + link_with: static_library('webengine', dependencies: dep_qt5, + include_directories: smolbote_include, + sources: [ 'webprofile.cpp', 'urlinterceptor.cpp', 'webprofilemanager.cpp', 'webpage.cpp', 'webview.cpp', 'webviewcontextmenu.cpp', webengine_moc ]) +) + +poi_sourceset.add(dep_webengine) + +test('profile', executable('profile', 'test/profile.cpp', dependencies: [ dep_qt5, dep_webengine, dep_catch ]), + env: { 'PROFILE' : meson.current_source_dir()/'test/testing.profile' }, + suite: 'webengine') + +test('profilemanager', executable('profilemanager', 'test/profilemanager.cpp', dependencies: [ dep_qt5, dep_webengine, dep_catch ]), + env: { 'PROFILES' : meson.current_source_dir()/'test/testing.profile' }, + suite: 'webengine') + +test('view', executable('view', 'test/view.cpp', dependencies: [ dep_qt5, dep_webengine, dep_catch ]), + args: [ '-platform', 'offscreen' ], + env: { 'PROFILE' : meson.current_source_dir()/'test/testing.profile', + 'URL' : meson.current_source_dir()/'test/sample.html' }, + suite: 'webengine') diff --git a/src/webengine/test/form.html b/src/webengine/test/form.html new file mode 100644 index 0000000..9d8b19e --- /dev/null +++ b/src/webengine/test/form.html @@ -0,0 +1,10 @@ +<html> + +<head> + <title>Form completion test</title> +</head> + +<body> +<h2>Form completion test</h2> +</body> +</html> diff --git a/src/webengine/test/icon.svg b/src/webengine/test/icon.svg new file mode 100644 index 0000000..a348cab --- /dev/null +++ b/src/webengine/test/icon.svg @@ -0,0 +1,7 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="300" height="300" version="1.1"> + <circle cx="150" cy="150" r="100" stroke="#000000" stroke-width="6" fill="#e60026"></circle> + <circle cx="150" cy="150" r="87" stroke="#000000" stroke-width="4" fill="#e5e4e2"></circle> + <path d="M230,150 A80,80 0 0 0 150,70 L150,150 Z" /> + <path d="M70,150 A80,80 0 0 0 150,230 L150,150 Z" /> +</svg> + diff --git a/src/webengine/test/profile.cpp b/src/webengine/test/profile.cpp new file mode 100644 index 0000000..7351f66 --- /dev/null +++ b/src/webengine/test/profile.cpp @@ -0,0 +1,124 @@ +#define CATCH_CONFIG_RUNNER + +// clazy:excludeall=non-pod-global-static + +#include "webprofilemanager.h" +#include <QApplication> +#include <catch2/catch.hpp> + +TEST_CASE("loading profile settings") +{ + const QString search = GENERATE(as<QString>{}, "https://search.url/t=%1", "https://duckduckgo.com/?q=%1&ia=web", "aaabbbccc"); + const QUrl homepage = GENERATE(as<QUrl>{}, "https://homepage.net", "about:blank", "aaabbbccc"); + const QUrl newtab = GENERATE(as<QUrl>{}, "https://newtab.net", "about:blank", "aaabbbccc"); + + auto *settings = WebProfile::load(QString(), search, homepage, newtab); + + REQUIRE(settings != nullptr); + REQUIRE(settings->value("search").toString() == search); + REQUIRE(settings->value("homepage").toUrl() == homepage); + REQUIRE(settings->value("newtab").toUrl() == newtab); + + delete settings; +} + +SCENARIO("loading individual profiles") +{ + GIVEN("no profile preset") + { + const QString search = GENERATE(as<QString>{}, "https://search.url/t=%1", "https://duckduckgo.com/?q=%1&ia=web", "aaabbbccc"); + const QUrl homepage = GENERATE(as<QUrl>{}, "https://homepage.net", "about:blank", "aaabbbccc"); + const QUrl newtab = GENERATE(as<QUrl>{}, "https://newtab.net", "about:blank", "aaabbbccc"); + + const QString id{ "id" }; + auto *settings = WebProfile::load(QString(), search, homepage, newtab); + auto *profile = WebProfile::load(id, settings, true); + + REQUIRE(profile != nullptr); + REQUIRE(profile->getId() == id); + REQUIRE(profile->property("id").toString() == id); + REQUIRE(profile->name() == id); + REQUIRE(profile->search() == search); + REQUIRE(profile->homepage() == homepage); + REQUIRE(profile->newtab() == newtab); + + REQUIRE(profile->isOffTheRecord()); + delete settings; + delete profile; + } + + GIVEN("an off-the-record profile preset") + { + REQUIRE(qEnvironmentVariableIsSet("PROFILE")); + + const QString id{ "id" }; + auto *settings = WebProfile::load(qgetenv("PROFILE"), QString(), QUrl(), QUrl()); + auto *profile = WebProfile::load(id, settings, true); + + REQUIRE(profile != nullptr); + REQUIRE(profile->getId() == id); + REQUIRE(profile->isOffTheRecord()); + + WHEN("created") + { + THEN("uses default values") + { + REQUIRE(profile->name() == "Test Profile"); + REQUIRE(profile->search() == "https://duckduckgo.com/?q=%1&ia=web"); + REQUIRE(profile->homepage() == QUrl("about:blank")); + REQUIRE(profile->newtab() == QUrl("about:blank")); + } + } + + WHEN("changing profile name") + { + const QString name = GENERATE(as<QString>{}, "a", "bb", "ccc"); + profile->setName(name); + THEN("the name changes") + { + REQUIRE(profile->name() == name); + REQUIRE(settings->value("name").toString() == name); + } + } + WHEN("changing search url") + { + const QString search = GENERATE(as<QString>{}, "a", "bb", "ccc"); + profile->setSearch(search); + THEN("the search url changes") + { + REQUIRE(profile->search() == search); + REQUIRE(settings->value("search").toString() == search); + } + } + WHEN("changing homepage") + { + const QUrl url = GENERATE(as<QUrl>{}, "a", "bb", "ccc"); + profile->setHomepage(url); + THEN("homepage changes") + { + REQUIRE(profile->homepage() == url); + REQUIRE(settings->value("homepage").toUrl() == url); + } + } + WHEN("changing newtab") + { + const QUrl url = GENERATE(as<QUrl>{}, "a", "bb", "ccc"); + profile->setNewtab(url); + THEN("newtab changes") + { + REQUIRE(profile->newtab() == url); + REQUIRE(settings->value("newtab").toUrl() == url); + } + } + + delete settings; + delete profile; + } +} + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + const auto r = Catch::Session().run(argc, argv); + return r; +} diff --git a/src/webengine/test/profilemanager.cpp b/src/webengine/test/profilemanager.cpp new file mode 100644 index 0000000..dc7c903 --- /dev/null +++ b/src/webengine/test/profilemanager.cpp @@ -0,0 +1,120 @@ +#define CATCH_CONFIG_RUNNER + +// clazy:excludeall=non-pod-global-static + +#include "webprofilemanager.h" +#include <QApplication> +#include <catch2/catch.hpp> + +SCENARIO("WebProfileManager") +{ + const QString search{ "https://search.url/t=%1" }; + const QUrl homepage{ "https://homepage.net" }; + const QUrl newtab{ "https://newtab.net" }; + const QString default_id{ "default" }; + + GIVEN("an empty profile list") + { + WebProfileManager<false> profiles({}, default_id, search, homepage, newtab); + + REQUIRE(profiles.idList().count() == 1); + REQUIRE(profiles.profile(default_id) == WebProfile::defaultProfile()); + REQUIRE(profiles.profile("not-in-list") == nullptr); + + WHEN("adding a new profile") + { + const QString id{ "id" }; + auto *settings = WebProfile::load(QString(), search, homepage, newtab); + auto *profile = WebProfile::load(id, settings, true); + + THEN("doesn't add profile without settings") + { + profiles.add(id, profile, nullptr); + REQUIRE(profiles.idList().count() == 1); + } + + THEN("doesn't add settings without profile") + { + profiles.add(id, nullptr, settings); + REQUIRE(profiles.idList().count() == 1); + } + + THEN("adds new profile with settings") + { + profiles.add(id, profile, settings); + REQUIRE(profiles.idList().count() == 2); + } + } + + WHEN("moving") + { + WebProfileManager<false> other(std::move(profiles)); + THEN("moved has the same number of profiles") + { + REQUIRE(other.idList().count() == 1); + REQUIRE(other.profile(default_id) == WebProfile::defaultProfile()); + REQUIRE(other.profile("not-in-list") == nullptr); + } + } + } + + GIVEN("a number of profiles, default undefined") + { + REQUIRE(qEnvironmentVariableIsSet("PROFILES")); + + WebProfileManager<false> profiles(QString::fromLatin1(qgetenv("PROFILES")).split(';'), default_id, search, homepage, newtab); + + REQUIRE(profiles.idList().count() == 2); + REQUIRE(profiles.profile(default_id) == WebProfile::defaultProfile()); + REQUIRE(profiles.profile("testing") != nullptr); + REQUIRE(profiles.profile("not-in-list") == nullptr); + + WHEN("making global") + { + profiles.make_global(); + WebProfileManager other; + + THEN("global has the same number of profiles") + { + REQUIRE(other.idList().count() == 2); + REQUIRE(other.profile(default_id) == WebProfile::defaultProfile()); + REQUIRE(other.profile("testing") != nullptr); + REQUIRE(other.profile("not-in-list") == nullptr); + } + + THEN("walking has no nullptrs") + { + other.walk([](const QString &, WebProfile *profile, const QSettings *settings) { + REQUIRE(profile != nullptr); + REQUIRE(settings != nullptr); + }); + } + } + } + + GIVEN("a number of profiles, default defined") + { + REQUIRE(qEnvironmentVariableIsSet("PROFILES")); + + WebProfileManager<false> profiles(QString::fromLatin1(qgetenv("PROFILES")).split(';'), "testing", search, homepage, newtab); + + REQUIRE(profiles.idList().count() == 1); + REQUIRE(profiles.profile("testing") == WebProfile::defaultProfile()); + REQUIRE(profiles.profile("not-in-list") == nullptr); + + WHEN("walking") + { + profiles.walk([](const QString &, WebProfile *profile, const QSettings *settings) { + REQUIRE(profile != nullptr); + REQUIRE(settings != nullptr); + }); + } + } +} + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + const auto r = Catch::Session().run(argc, argv); + return r; +} diff --git a/src/webengine/test/sample.html b/src/webengine/test/sample.html new file mode 100644 index 0000000..54746d5 --- /dev/null +++ b/src/webengine/test/sample.html @@ -0,0 +1,7 @@ +<html> +<head><title>sample page</title></head> +<body> + <h2><img src="icon.svg" />This is a sample page</h2> + <iframe width="100%" height="80%" src="form.html"></iframe> +</body> +</html> diff --git a/src/webengine/test/testing.profile b/src/webengine/test/testing.profile new file mode 100644 index 0000000..e345a3e --- /dev/null +++ b/src/webengine/test/testing.profile @@ -0,0 +1,8 @@ +name=Test Profile +otr=true +search=https://duckduckgo.com/?q=%1&ia=web +homepage=about:blank +newtab=about:blank + +[headers] +Dnt=1 diff --git a/src/webengine/test/view.cpp b/src/webengine/test/view.cpp new file mode 100644 index 0000000..8aa639a --- /dev/null +++ b/src/webengine/test/view.cpp @@ -0,0 +1,92 @@ +#define CATCH_CONFIG_RUNNER + +// clazy:excludeall=non-pod-global-static + +#include "webprofile.h" +#include "webview.h" +#include <QApplication> +#include <QMainWindow> +#include <QTimer> +#include <QtWebEngine> +#include <catch2/catch.hpp> + +SCENARIO("WebView") +{ + const QString profile_id{ "default" }; + auto *settings = WebProfile::load(qgetenv("PROFILE"), "about:blank", QUrl{ "about:blank" }, QUrl{ "about:blank" }); + auto *profile = WebProfile::load(profile_id, settings, true); + + QMainWindow window; + auto *view = new WebView(profile, nullptr); + window.setCentralWidget(view); + window.show(); + window.resize(800, 600); + + WHEN("created") + { + THEN("using the default profile") + { + REQUIRE(view->profile() == profile); + } + THEN("serialized using default profile") + { + const auto data = view->serialize(); + REQUIRE(data.profile == profile_id); + REQUIRE(data.url.isEmpty()); + REQUIRE(!data.history.isEmpty()); + } + THEN("loading a url") + { + // block until a loadFinished signal + QEventLoop pause; + QObject::connect(view, &WebView::loadFinished, &pause, &QEventLoop::quit); + view->load(QUrl{ qgetenv("URL") }); + pause.exec(); + + REQUIRE(view->isLoaded()); + } + } + + WHEN("changing profiles") + { + const QString swap_profile_id{ "swap_profile" }; + auto *swap_settings = WebProfile::load(QString(), "about:blank", QUrl{ "about:blank" }, QUrl{ "about:blank" }); + auto *swap_profile = WebProfile::load(swap_profile_id, swap_settings, true); + + view->setProfile(swap_profile); + THEN("using the swap profile") + { + REQUIRE(view->profile() == swap_profile); + } + THEN("serialized using swap profile") + { + const auto data = view->serialize(); + REQUIRE(data.profile == swap_profile_id); + REQUIRE(data.url.isEmpty()); + REQUIRE(!data.history.isEmpty()); + } + + view->setProfile(profile); + delete swap_settings; + delete swap_profile; + } + + // cleanup + window.close(); + delete view; + delete settings; + delete profile; +} + +int main(int argc, char **argv) +{ + QtWebEngine::initialize(); + QApplication app(argc, argv); + + QTimer::singleShot(0, &app, [argc, argv, &app]() { + const auto n_failed = Catch::Session().run(argc, argv); + app.exit(n_failed); + }); + + return app.exec(); +} diff --git a/src/webengine/urlinterceptor.cpp b/src/webengine/urlinterceptor.cpp index 29cd869..047cad4 100644 --- a/src/webengine/urlinterceptor.cpp +++ b/src/webengine/urlinterceptor.cpp @@ -8,59 +8,24 @@ #include "urlinterceptor.h" #include "webprofile.h" -#include "urlfilter.h" // test DNT on https://browserleaks.com/donottrack -UrlRequestInterceptor::UrlRequestInterceptor(WebProfile* profile, QObject* parent) - : QWebEngineUrlRequestInterceptor(parent) +UrlRequestInterceptor::UrlRequestInterceptor(WebProfile* profile) + : QWebEngineUrlRequestInterceptor(profile) { Q_CHECK_PTR(profile); m_profile = profile; } -void UrlRequestInterceptor::addHttpHeader(const QByteArray &key, const QByteArray &value) -{ - headers.append(qMakePair(key, value)); -} - -void UrlRequestInterceptor::addFilter(UrlFilter *filter) -{ - if(filter != nullptr) - filters.append(filter); -} -void UrlRequestInterceptor::removeFilter(UrlFilter *filter) -{ - if(filter != nullptr) - filters.removeOne(filter); -} - void UrlRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo &info) { - for(const auto *filter : qAsConst(filters)) { - const auto match = filter->match(info.firstPartyUrl(), info.requestUrl(), info.resourceType()); - - // skip if no match - if(match.first == UrlFilter::NotMatched) - continue; - - else { - if(match.first == UrlFilter::Allow) - info.block(false); - else if(match.first == UrlFilter::Block) - info.block(true); - else if(match.first == UrlFilter::Redirect) - info.redirect(QUrl::fromUserInput(match.second)); - // we found a match, skip the rest - break; - } + for(auto *filter : qAsConst(m_profile->m_filters)) { + filter->interceptRequest(info); } // set headers - for(const auto &header : qAsConst(headers)) { - info.setHttpHeader(header.first, header.second); - } - for(auto i = m_profile->headers().constBegin(); i != m_profile->headers().constEnd(); ++i) { + for(auto i = m_profile->m_headers.constBegin(); i != m_profile->m_headers.constEnd(); ++i) { info.setHttpHeader(i.key(), i.value()); } } diff --git a/src/webengine/urlinterceptor.h b/src/webengine/urlinterceptor.h index 4909586..eb3ce67 100644 --- a/src/webengine/urlinterceptor.h +++ b/src/webengine/urlinterceptor.h @@ -9,30 +9,23 @@ #ifndef SMOLBOTE_URLREQUESTINTERCEPTOR_H #define SMOLBOTE_URLREQUESTINTERCEPTOR_H -#include <QVector> #include <QWebEngineUrlRequestInterceptor> -#include <QByteArray> -class UrlFilter; class WebProfile; class UrlRequestInterceptor : public QWebEngineUrlRequestInterceptor { - Q_OBJECT + friend class WebProfile; + public: - explicit UrlRequestInterceptor(WebProfile *profile, QObject *parent = nullptr); ~UrlRequestInterceptor() override = default; - void addHttpHeader(const QByteArray &key, const QByteArray &value); - - void addFilter(UrlFilter *filter); - void removeFilter(UrlFilter *filter); - void interceptRequest(QWebEngineUrlRequestInfo &info) override; +protected: + explicit UrlRequestInterceptor(WebProfile *profile); + private: WebProfile *m_profile; - QVector<QPair<QByteArray, QByteArray>> headers; - QVector<UrlFilter*> filters; }; #endif // SMOLBOTE_URLREQUESTINTERCEPTOR_H diff --git a/src/webengine/webpage.cpp b/src/webengine/webpage.cpp index e79db9d..8c6b8db 100644 --- a/src/webengine/webpage.cpp +++ b/src/webengine/webpage.cpp @@ -12,7 +12,6 @@ #include <QTimer> #include <QWebEngineFullScreenRequest> #include <QWebEngineCertificateError> -#include <spdlog/spdlog.h> QString tr_terminationStatus(QWebEnginePage::RenderProcessTerminationStatus status) { @@ -106,17 +105,16 @@ void WebPage::featurePermissionDialog(const QUrl &securityOrigin, QWebEnginePage messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); messageBox.setDefaultButton(QMessageBox::No); - if(messageBox.exec() == QMessageBox::Yes) + if(messageBox.exec() == QMessageBox::Yes) { setFeaturePermission(securityOrigin, feature, QWebEnginePage::PermissionGrantedByUser); - else + } else { setFeaturePermission(securityOrigin, feature, QWebEnginePage::PermissionDeniedByUser); + } } void WebPage::renderProcessCrashed(QWebEnginePage::RenderProcessTerminationStatus terminationStatus, int exitCode) { if(terminationStatus != QWebEnginePage::NormalTerminationStatus) { - spdlog::warn("render process terminated: [{}] {}", terminationStatus, exitCode); - QString page = "<html><body><h1>This tab has crashed!</h1>%message%</body></html>"; page.replace(QLatin1String("%message%"), QString("<p>%1<br>Exit code is %2.</p>" "<p>Press <a href='%3'>here</a> to reload this tab.</p>") diff --git a/src/webengine/webpage.h b/src/webengine/webpage.h index 48011cb..91ae4f3 100644 --- a/src/webengine/webpage.h +++ b/src/webengine/webpage.h @@ -14,13 +14,15 @@ class WebPage : public QWebEnginePage { Q_OBJECT + public: - explicit WebPage(QWebEngineProfile *profile, QObject *parent = nullptr); + WebPage(QWebEngineProfile *profile, QObject *parent = nullptr); + ~WebPage() override = default; protected: bool certificateError(const QWebEngineCertificateError &certificateError) override; -private slots: +protected slots: void featurePermissionDialog(const QUrl &securityOrigin, QWebEnginePage::Feature feature); void renderProcessCrashed(QWebEnginePage::RenderProcessTerminationStatus terminationStatus, int exitCode); }; diff --git a/src/webengine/webprofile.cpp b/src/webengine/webprofile.cpp index 5224189..f0368c9 100644 --- a/src/webengine/webprofile.cpp +++ b/src/webengine/webprofile.cpp @@ -11,6 +11,7 @@ #include <QSettings> #include <QWebEngineCookieStore> #include <QWebEngineSettings> +#include "urlinterceptor.h" static WebProfile *s_profile = nullptr; @@ -25,11 +26,89 @@ WebProfile *WebProfile::defaultProfile() return s_profile; } -WebProfile::WebProfile(const QString &name, QObject *parent) - : QWebEngineProfile(parent) +QSettings *WebProfile::load(const QString &path, const QString &search, const QUrl &homepage, const QUrl &newtab) +{ + auto *settings = new QSettings(path, QSettings::IniFormat); + + if(!settings->contains("search")) { + settings->setValue("search", search); + } + if(!settings->contains("homepage")) { + settings->setValue("homepage", homepage); + } + if(!settings->contains("newtab")) { + settings->setValue("newtab", newtab); + } + + return settings; +} + +WebProfile *WebProfile::load(const QString &id, QSettings *settings, bool isOffTheRecord) { - m_name = name; + WebProfile *profile = nullptr; + if(isOffTheRecord || settings->value("otr", isOffTheRecord).toBool()) { + profile = new WebProfile(id, nullptr); + } else { + profile = new WebProfile(id, id, nullptr); + } + + profile->m_name = settings->value("name", id).toString(); + connect(profile, &WebProfile::nameChanged, profile, [settings](const QString &name) { settings->setValue("name", name); }); + profile->m_search = settings->value("search", "").toString(); + connect(profile, &WebProfile::searchChanged, settings, [settings](const QString &url) { settings->setValue("search", url); }); + profile->m_homepage = settings->value("homepage", "").toUrl(); + connect(profile, &WebProfile::homepageChanged, settings, [settings](const QUrl &url) { settings->setValue("homepage", url); }); + profile->m_newtab = settings->value("newtab", "").toUrl(); + connect(profile, &WebProfile::newtabChanged, settings, [settings](const QUrl &url) { settings->setValue("newtab", url); }); + + { + settings->beginGroup("properties"); + const auto keys = settings->childKeys(); + for(const QString &key : keys) { + profile->setProperty(qUtf8Printable(key), settings->value(key)); + } + settings->endGroup(); // properties + connect(profile, &WebProfile::propertyChanged, [settings](const QString &property, const QVariant &value) { + settings->setValue("properties/" + property, value); + }); + } + { + settings->beginGroup("attributes"); + const auto keys = settings->childKeys(); + auto *s = profile->settings(); + for(const QString &key : keys) { + auto attribute = static_cast<QWebEngineSettings::WebAttribute>(key.toInt()); + s->setAttribute(attribute, settings->value(key).toBool()); + } + settings->endGroup(); + connect(profile, &WebProfile::attributeChanged, [settings](const QWebEngineSettings::WebAttribute attr, const bool value) { + settings->setValue("attributes/" + QString::number(attr), value); + }); + } + { + // headers + settings->beginGroup("headers"); + for(const QString &key : settings->childKeys()) { + profile->setHttpHeader(key.toLatin1(), settings->value(key).toString().toLatin1()); + } + settings->endGroup(); + connect(profile, &WebProfile::headerChanged, [settings](const QString &name, const QString &value) { + settings->setValue("headers/" + name, value); + }); + connect(profile, &WebProfile::headerRemoved, [settings](const QString &name) { + settings->remove("headers/" + name); + }); + } + return profile; +} + +// off-the-record constructor +WebProfile::WebProfile(const QString &id, QObject *parent) + : QWebEngineProfile(parent) + , m_id(id) +{ + QWebEngineProfile::setUrlRequestInterceptor(new UrlRequestInterceptor(this)); connect(this->cookieStore(), &QWebEngineCookieStore::cookieAdded, this, [this](const QNetworkCookie &cookie) { m_cookies.append(cookie); }); @@ -38,11 +117,12 @@ WebProfile::WebProfile(const QString &name, QObject *parent) }); } -WebProfile::WebProfile(const QString &storageName, const QString &name, QObject *parent) +// default constructor +WebProfile::WebProfile(const QString &id, const QString &storageName, QObject *parent) : QWebEngineProfile(storageName, parent) + , m_id(id) { - m_name = name; - + QWebEngineProfile::setUrlRequestInterceptor(new UrlRequestInterceptor(this)); connect(this->cookieStore(), &QWebEngineCookieStore::cookieAdded, this, [this](const QNetworkCookie &cookie) { m_cookies.append(cookie); }); @@ -50,109 +130,3 @@ WebProfile::WebProfile(const QString &storageName, const QString &name, QObject m_cookies.removeAll(cookie); }); } - -const QString WebProfile::name() const -{ - return m_name; -} - -void WebProfile::setName(const QString &name) -{ - m_name = name; - emit nameChanged(name); -} - -QString WebProfile::search() const -{ - return m_search; -} - -void WebProfile::setSearch(const QString &url) -{ - m_search = url; - emit searchChanged(m_search); -} - -QUrl WebProfile::homepage() const -{ - return m_homepage; -} - -void WebProfile::setHomepage(const QUrl &url) -{ - m_homepage = url; - emit homepageChanged(m_homepage); -} - -QUrl WebProfile::newtab() const -{ - return m_newtab; -} - -void WebProfile::setNewtab(const QUrl &url) -{ - m_newtab = url; - emit newtabChanged(m_newtab); -} - -void WebProfile::setCachePath(const QString &path) -{ - QWebEngineProfile::setCachePath(path); - emit propertyChanged("cachePath", path); -} - -void WebProfile::setPersistentStoragePath(const QString &path) -{ - QWebEngineProfile::setPersistentStoragePath(path); - emit propertyChanged("persistentStoragePath", path); -} - -void WebProfile::setPersistentCookiesPolicy(int policy) -{ - QWebEngineProfile::setPersistentCookiesPolicy(static_cast<QWebEngineProfile::PersistentCookiesPolicy>(policy)); - emit propertyChanged("persistentCookiesPolicy", policy); -} - -void WebProfile::setHttpAcceptLanguage(const QString &httpAcceptLanguage) -{ - QWebEngineProfile::setHttpAcceptLanguage(httpAcceptLanguage); - emit propertyChanged("httpAcceptLanguage", httpAcceptLanguage); -} - -void WebProfile::setHttpCacheMaximumSize(int maxSize) -{ - QWebEngineProfile::setHttpCacheMaximumSize(maxSize); - emit propertyChanged("httpCacheMaximumSize", maxSize); -} - -void WebProfile::setHttpCacheType(int type) -{ - QWebEngineProfile::setHttpCacheType(static_cast<QWebEngineProfile::HttpCacheType>(type)); - emit propertyChanged("httpCacheType", type); -} - -void WebProfile::setHttpUserAgent(const QString &userAgent) -{ - QWebEngineProfile::setHttpUserAgent(userAgent); - emit propertyChanged("httpUserAgent", userAgent); -} - -void WebProfile::setHttpHeader(const QString &name, const QString &value) -{ - m_headers[name.toLatin1()] = value.toLatin1(); - emit headerChanged(name, value); -} - -void WebProfile::removeHttpHeader(const QString &name) -{ - if(m_headers.contains(name.toLatin1())) { - m_headers.remove(name.toLatin1()); - emit headerRemoved(name); - } -} - -void WebProfile::setSpellCheckEnabled(bool enable) -{ - QWebEngineProfile::setSpellCheckEnabled(enable); - emit propertyChanged("spellCheckEnabed", enable); -} diff --git a/src/webengine/webprofile.h b/src/webengine/webprofile.h index 66154af..180cc9d 100644 --- a/src/webengine/webprofile.h +++ b/src/webengine/webprofile.h @@ -9,7 +9,6 @@ #ifndef SMOLBOTE_WEBENGINEPROFILE_H #define SMOLBOTE_WEBENGINEPROFILE_H -#include <QHash> #include <QMap> #include <QNetworkCookie> #include <QString> @@ -17,14 +16,17 @@ #include <QVector> #include <QWebEngineProfile> #include <QWebEngineSettings> +#include <QVariant> +#include <QSettings> -class WebProfileManager; +class UrlRequestInterceptor; class WebProfile : public QWebEngineProfile { - friend class WebProfileManager; + friend class UrlRequestInterceptor; Q_OBJECT + Q_PROPERTY(QString id READ getId CONSTANT) Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) Q_PROPERTY(QString search READ search WRITE setSearch NOTIFY searchChanged) Q_PROPERTY(QUrl homepage READ homepage WRITE setHomepage NOTIFY homepageChanged) @@ -42,72 +44,153 @@ class WebProfile : public QWebEngineProfile Q_PROPERTY(bool spellCheckEnabled READ isSpellCheckEnabled WRITE setSpellCheckEnabled NOTIFY propertyChanged) +signals: + void nameChanged(const QString &name); + void searchChanged(const QString &url); + void homepageChanged(const QUrl &url); + void newtabChanged(const QUrl &url); + + void propertyChanged(const QString &name, const QVariant &value); + void attributeChanged(QWebEngineSettings::WebAttribute attribute, bool value); + void headerChanged(const QString &name, const QString &value); + void headerRemoved(const QString &name); + public: + [[nodiscard]] static QSettings *load(const QString &path, const QString &search = QString(), const QUrl &homepage = QUrl(), const QUrl &newtab = QUrl()); + [[nodiscard]] static WebProfile *load(const QString &id, QSettings *settings, bool isOffTheRecord = true); + + WebProfile(const WebProfile &) = delete; + WebProfile& operator=(const WebProfile &) = delete; + WebProfile(WebProfile &&) = delete; + WebProfile& operator=(WebProfile &&) = delete; + static WebProfile *defaultProfile(); static void setDefaultProfile(WebProfile *profile); - ~WebProfile() = default; + ~WebProfile() override = default; - const QString name() const; - void setName(const QString &name); - - const QVector<QNetworkCookie> cookies() const + [[nodiscard]] QString getId() const { - return qAsConst(m_cookies); + return m_id; + } + [[nodiscard]] QString name() const + { + return m_name; } - const QMap<QByteArray, QByteArray> headers() const + void setName(const QString &name) { - return qAsConst(m_headers); + m_name = name; + emit nameChanged(name); } - // search url - QString search() const; - void setSearch(const QString &url); + [[nodiscard]] QVector<QNetworkCookie> cookies() const + { + return qAsConst(m_cookies); + } - // homepage url - QUrl homepage() const; - void setHomepage(const QUrl &url); + [[nodiscard]] QString search() const + { + return m_search; + } + void setSearch(const QString &url) + { + m_search = url; + emit searchChanged(m_search); + } - // new tab url - QUrl newtab() const; - void setNewtab(const QUrl &url); + [[nodiscard]] QUrl homepage() const + { + return m_homepage; + } + void setHomepage(const QUrl &url) + { + m_homepage = url; + emit homepageChanged(m_homepage); + } - void setCachePath(const QString &path); - void setPersistentStoragePath(const QString &path); - void setPersistentCookiesPolicy(int policy); + [[nodiscard]] QUrl newtab() const + { + return m_newtab; + } + void setNewtab(const QUrl &url) + { + m_newtab = url; + emit newtabChanged(m_newtab); + } - void setHttpAcceptLanguage(const QString &httpAcceptLanguage); - void setHttpCacheMaximumSize(int maxSize); - void setHttpCacheType(int type); - void setHttpUserAgent(const QString &userAgent); - void setHttpHeader(const QString &name, const QString &value); - void removeHttpHeader(const QString &name); + void setCachePath(const QString &path) + { + QWebEngineProfile::setCachePath(path); + emit propertyChanged("cachePath", path); + } + void setPersistentStoragePath(const QString &path) + { + QWebEngineProfile::setPersistentStoragePath(path); + emit propertyChanged("persistentStoragePath", path); + } + void setPersistentCookiesPolicy(int policy) + { + QWebEngineProfile::setPersistentCookiesPolicy(static_cast<QWebEngineProfile::PersistentCookiesPolicy>(policy)); + emit propertyChanged("persistentCookiesPolicy", policy); + } - void setSpellCheckEnabled(bool enable); + void setHttpAcceptLanguage(const QString &httpAcceptLanguage) + { + QWebEngineProfile::setHttpAcceptLanguage(httpAcceptLanguage); + emit propertyChanged("httpAcceptLanguage", httpAcceptLanguage); + } + void setHttpCacheMaximumSize(int maxSize) + { + QWebEngineProfile::setHttpCacheMaximumSize(maxSize); + emit propertyChanged("httpCacheMaximumSize", maxSize); + } + void setHttpCacheType(int type) + { + QWebEngineProfile::setHttpCacheType(static_cast<QWebEngineProfile::HttpCacheType>(type)); + emit propertyChanged("httpCacheType", type); + } + void setHttpUserAgent(const QString &userAgent) + { + QWebEngineProfile::setHttpUserAgent(userAgent); + emit propertyChanged("httpUserAgent", userAgent); + } + void setHttpHeader(const QString &name, const QString &value) + { + m_headers[name.toLatin1()] = value.toLatin1(); + emit headerChanged(name, value); + } + void removeHttpHeader(const QString &name) + { + if(m_headers.contains(name.toLatin1())) { + m_headers.remove(name.toLatin1()); + emit headerRemoved(name); + } + } -signals: - void nameChanged(const QString &name); - void searchChanged(const QString &url); - void homepageChanged(const QUrl &url); - void newtabChanged(const QUrl &url); + void setSpellCheckEnabled(bool enable) + { + QWebEngineProfile::setSpellCheckEnabled(enable); + emit propertyChanged("spellCheckEnabed", enable); + } - void propertyChanged(const QString &name, const QVariant &value); - void attributeChanged(const QWebEngineSettings::WebAttribute attribute, const bool value); - void headerChanged(const QString &name, const QString &value); - void headerRemoved(const QString &name); + void setUrlRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor) + { + m_filters.append(interceptor); + } protected: // off-the-record constructor - explicit WebProfile(const QString &name, QObject *parent = nullptr); + explicit WebProfile(const QString &id, QObject *parent = nullptr); // default constructor - explicit WebProfile(const QString &storageName, const QString &name, QObject *parent = nullptr); + explicit WebProfile(const QString &id, const QString &storageName, QObject *parent = nullptr); -private: + const QString m_id; QString m_name; QString m_search = QString("about:blank"); QUrl m_homepage = QUrl("about:blank"); QUrl m_newtab = QUrl("about:blank"); + QVector<QWebEngineUrlRequestInterceptor*> m_filters; QVector<QNetworkCookie> m_cookies; QMap<QByteArray, QByteArray> m_headers; }; diff --git a/src/webengine/webprofilemanager.cpp b/src/webengine/webprofilemanager.cpp index bdd10f0..785251b 100644 --- a/src/webengine/webprofilemanager.cpp +++ b/src/webengine/webprofilemanager.cpp @@ -7,28 +7,23 @@ */ #include "webprofilemanager.h" -#include "configuration.h" #include "webprofile.h" -#include <QFileInfo> -#include <QWebEngineSettings> -static WebProfileManager *s_instance = nullptr; +static WebProfileManager<false> *s_instance = nullptr; -auto WebProfileManager::instance() -> const WebProfileManager * +template <> +void WebProfileManager<false>::make_global() { - return s_instance; -} -void WebProfileManager::setInstance(WebProfileManager *ptr) -{ - s_instance = ptr; + if(s_instance == nullptr) { + s_instance = this; + } } -WebProfileManager::WebProfileManager(QObject *parent) - : QObject(parent) -{ -} +template <> +WebProfileManager<true>::~WebProfileManager() = default; -WebProfileManager::~WebProfileManager() +template <> +WebProfileManager<false>::~WebProfileManager() { for(Profile p : qAsConst(profiles)) { if(p.selfDestruct && p.settings != nullptr) { @@ -48,111 +43,30 @@ WebProfileManager::~WebProfileManager() } } -WebProfile *WebProfileManager::profile(const QString &id) const +template <> +WebProfile *WebProfileManager<true>::profile(const QString &id) const { - // Check if profile exists - if(profiles.contains(id)) { - return profiles.value(id).ptr; - } - - return nullptr; + return s_instance->profile(id); } -WebProfile *WebProfileManager::add(const QString &id, const QString &path, bool isOffTheRecord) +template <> +QStringList WebProfileManager<true>::idList() const { - if(profiles.contains(id)) { - return nullptr; - } - - Configuration conf; - Profile profile; - - if(!path.isEmpty()) - profile.settings = new QSettings(path, QSettings::IniFormat); - else - profile.settings = new QSettings; + return s_instance->idList(); +} - // QWebEngineCore cleans up profiles automatically, so no need to set parent - if(profile.settings->value("otr", isOffTheRecord).toBool()) { - // name parent - profile.ptr = new WebProfile(profile.settings->value("name", id).toString(), nullptr); - } else { - // storageName name parent - profile.ptr = new WebProfile(id, profile.settings->value("name", id).toString(), nullptr); +template <> +void WebProfileManager<false>::walk(std::function<void(const QString &id, WebProfile *profile, const QSettings *settings)> f) const +{ + for(auto iter = profiles.begin(); iter != profiles.end(); ++iter) { + f(iter.key(), iter.value().ptr, iter.value().settings); } - - profile.settings->setParent(profile.ptr); - - connect(profile.ptr, &WebProfile::nameChanged, profile.settings, [profile](const QString &name) { - profile.settings->setValue("name", name); - }); - - profile.ptr->setSearch(profile.settings->value("search", conf.value<QString>("profile.search").value()).toString()); - connect(profile.ptr, &WebProfile::searchChanged, profile.settings, [profile](const QString &url) { - profile.settings->setValue("search", url); - }); - - profile.ptr->setHomepage(profile.settings->value("homepage", conf.value<QString>("profile.homepage").value()).toUrl()); - connect(profile.ptr, &WebProfile::homepageChanged, profile.settings, [profile](const QUrl &url) { - profile.settings->setValue("homepage", url); - }); - - profile.ptr->setNewtab(profile.settings->value("newtab", conf.value<QString>("profile.newtab").value()).toUrl()); - connect(profile.ptr, &WebProfile::newtabChanged, profile.settings, [profile](const QUrl &url) { - profile.settings->setValue("newtab", url); - }); - - if(profile.settings != nullptr) { - profile.settings->beginGroup("properties"); - { - const auto keys = profile.settings->childKeys(); - for(const QString &key : keys) { - profile.ptr->setProperty(qUtf8Printable(key), profile.settings->value(key)); - } - } - profile.settings->endGroup(); // properties - connect(profile.ptr, &WebProfile::propertyChanged, [profile](const QString &property, const QVariant &value) { - profile.settings->setValue("properties/" + property, value); - }); - - profile.settings->beginGroup("attributes"); - { - const auto keys = profile.settings->childKeys(); - auto *settings = profile.ptr->settings(); - for(const QString &key : keys) { - auto attribute = static_cast<QWebEngineSettings::WebAttribute>(key.toInt()); - settings->setAttribute(attribute, profile.settings->value(key).toBool()); - } - } - profile.settings->endGroup(); - connect(profile.ptr, &WebProfile::attributeChanged, [profile](const QWebEngineSettings::WebAttribute attr, const bool value) { - profile.settings->setValue("attributes/" + QString::number(attr), value); - }); - - // headers - profile.settings->beginGroup("headers"); - for(const QString &key : profile.settings->childKeys()) { - profile.ptr->setHttpHeader(key.toLatin1(), profile.settings->value(key).toString().toLatin1()); - } - profile.settings->endGroup(); - connect(profile.ptr, &WebProfile::headerChanged, [profile](const QString &name, const QString &value) { - profile.settings->setValue("headers/" + name, value); - }); - connect(profile.ptr, &WebProfile::headerRemoved, [profile](const QString &name) { - profile.settings->remove("headers/" + name); - }); - - } // profile.settings != nullptr - - profiles[id] = profile; - return profile.ptr; } -void WebProfileManager::deleteProfile(const QString &id) +template <> +void WebProfileManager<true>::walk(std::function<void(const QString &id, WebProfile *profile, const QSettings *settings)> f) const { - if(profiles.contains(id)) { - profiles[id].selfDestruct = true; - } + s_instance->walk(f); } void profileMenu(QMenu *menu, const std::function<void(WebProfile *)> &callback, WebProfile *current, bool checkable) @@ -160,13 +74,10 @@ void profileMenu(QMenu *menu, const std::function<void(WebProfile *)> &callback, auto *group = new QActionGroup(menu); QObject::connect(menu, &QMenu::aboutToHide, group, &QActionGroup::deleteLater); - for(const auto &profile : qAsConst(s_instance->profiles)) { - auto *action = menu->addAction(profile.ptr->name(), profile.ptr, [profile, callback]() { - callback(profile.ptr); - }); + s_instance->walk([=](const QString &, WebProfile *profile, const QSettings *) { + auto *action = menu->addAction(profile->name(), profile, [=]() { callback(profile); }); action->setCheckable(checkable); - if(profile.ptr == current) - action->setChecked(true); + action->setChecked(profile == current); group->addAction(action); - } + }); } diff --git a/src/webengine/webprofilemanager.h b/src/webengine/webprofilemanager.h index 0e18d5f..22fb31c 100644 --- a/src/webengine/webprofilemanager.h +++ b/src/webengine/webprofilemanager.h @@ -9,63 +9,97 @@ #ifndef SMOLBOTE_WEBPROFILEMANAGER_H #define SMOLBOTE_WEBPROFILEMANAGER_H +#include "singleton.hpp" #include "webprofile.h" #include <QDir> #include <QFile> +#include <QFileInfo> #include <QMap> #include <QMenu> -#include <QObject> -#include <QSettings> #include <functional> void profileMenu(QMenu *menu, const std::function<void(WebProfile *)> &callback, WebProfile *current = nullptr, bool checkable = false); -class Browser; -class WebProfileManager : public QObject +template <bool use_global = true> +class consumable(unconsumed) WebProfileManager { - Q_OBJECT - - friend class Browser; - friend void profileMenu(QMenu *, const std::function<void(WebProfile *)> &, WebProfile *current, bool); - public: - explicit WebProfileManager(QObject *parent); - ~WebProfileManager() override; + return_typestate(unconsumed) WebProfileManager() + { + static_assert(use_global); + } + return_typestate(unconsumed) WebProfileManager(const QStringList &paths, const QString &default_id, + const QString &search = QString(), const QUrl &homepage = QUrl(), const QUrl &newtab = QUrl()) + { + static_assert(!use_global); + for(const auto &path : paths) { + const auto id = QFileInfo(path).baseName(); + Profile profile; + profile.settings = WebProfile::load(path, search, homepage, newtab); + profile.ptr = WebProfile::load(id, profile.settings, true); + profiles[id] = profile; + } - static auto instance() -> const WebProfileManager *; - static void setInstance(WebProfileManager *ptr); + if(!profiles.contains(default_id)) { + Profile profile; + profile.settings = WebProfile::load(QString(), search, homepage, newtab); + profile.ptr = WebProfile::load(default_id, profile.settings, true); + profiles[default_id] = profile; + } + WebProfile::setDefaultProfile(profiles[default_id].ptr); + } + ~WebProfileManager(); - /** Create a profile with specified id - * param id The profile ID - * return WebProfile* The profile, or nullptr if one could not be created - */ - WebProfile *profile(const QString &id) const; + WebProfileManager(const WebProfileManager &) = delete; + WebProfileManager &operator=(const WebProfileManager &) = delete; - /** Set a profile for deletion - * param id The profile ID - * return void - */ - [[deprecated]] void deleteProfile(const QString &id); + return_typestate(unconsumed) WebProfileManager(WebProfileManager<false> && other param_typestate(unconsumed)) + { + static_assert(!use_global); + profiles = std::move(other.profiles); + other.consume(); + } + WebProfileManager &operator=(WebProfileManager &&) = delete; - const QStringList idList() const + callable_when(unconsumed) [[nodiscard]] WebProfile *profile(const QString &id) const { - return profiles.keys(); + return profiles.value(id).ptr; + } + + callable_when(unconsumed) void add(const QString &id, WebProfile *profile, QSettings *settings) + { + if constexpr(use_global) { + return; + } + + if(profile != nullptr && settings != nullptr) { + profiles[id] = Profile{ profile, settings, false }; + } } - QString id(WebProfile *profile) const + + callable_when(unconsumed) void deleteProfile(const QString &id) { - QMapIterator<QString, Profile> i(profiles); - while(i.hasNext()) { - i.next(); - if(i.value().ptr == profile) - return i.key(); + if constexpr(use_global) { + return; + } + + if(profiles.contains(id)) { + profiles[id].selfDestruct = true; } - return QString(); } -protected: - WebProfile *add(const QString &id, const QString &path = QString(), bool isOffTheRecord = true); + callable_when(unconsumed) [[nodiscard]] QStringList idList() const + { + return profiles.keys(); + } + + callable_when(unconsumed) void walk(std::function<void(const QString &id, WebProfile *profile, const QSettings *settings)>) const; + + callable_when(unconsumed) void make_global(); private: + set_typestate(consumed) void consume() {} + struct Profile { WebProfile *ptr = nullptr; QSettings *settings = nullptr; diff --git a/src/webengine/webview.cpp b/src/webengine/webview.cpp index c64333e..135f25c 100644 --- a/src/webengine/webview.cpp +++ b/src/webengine/webview.cpp @@ -7,20 +7,16 @@ */ #include "webview.h" -#include "subwindow/subwindow.h" #include "webpage.h" #include "webprofile.h" #include "webprofilemanager.h" #include "webviewcontextmenu.h" #include <QContextMenuEvent> -#include <QJsonObject> #include <QWebEngineHistoryItem> WebView::WebView(QWidget *parent) : QWebEngineView(parent) { - m_parentWindow = qobject_cast<SubWindow *>(parent); - // load status and progress connect(this, &QWebEngineView::loadStarted, this, [this]() { m_loaded = false; @@ -37,21 +33,22 @@ WebView::WebView(QWidget *parent) }); } -WebView::WebView(WebProfile *profile, QWidget *parent) +WebView::WebView(WebProfile *profile, cb_createWindow_t cb, QWidget *parent) : WebView(parent) { + cb_createWindow = cb; Q_CHECK_PTR(profile); m_profile = profile; setPage(new WebPage(profile, this)); } -WebView::WebView(const Session::WebView &data, QWidget *parent) +WebView::WebView(const Session::WebView &data, cb_createWindow_t cb, QWidget *parent) : WebView(parent) { - const auto *profileManager = WebProfileManager::instance(); - Q_CHECK_PTR(profileManager); + cb_createWindow = cb; + WebProfileManager profileManager; - auto *profile = profileManager->profile(data.profile); + auto *profile = profileManager.profile(data.profile); if(profile != nullptr) { setProfile(profile); } @@ -75,19 +72,11 @@ void WebView::setProfile(WebProfile *profile) Session::WebView WebView::serialize() const { - const auto *profileManager = WebProfileManager::instance(); - Q_CHECK_PTR(profileManager); - QByteArray historyData; QDataStream historyStream(&historyData, QIODevice::WriteOnly); historyStream << *history(); - return { profileManager->id(profile()), QString(), historyData }; -} - -bool WebView::isLoaded() const -{ - return m_loaded; + return { profile()->getId(), QString(), historyData }; } void WebView::search(const QString &term) @@ -96,35 +85,6 @@ void WebView::search(const QString &term) load(searchUrl); } -WebView *WebView::createWindow(QWebEnginePage::WebWindowType type) -{ - Q_CHECK_PTR(m_parentWindow); - - // parent Window has been found - auto index = m_parentWindow->addTab(); - WebView *view = m_parentWindow->view(index); - switch(type) { - case QWebEnginePage::WebBrowserWindow: - // a complete web browser window - break; - - case QWebEnginePage::WebBrowserTab: - // a web browser tab - m_parentWindow->setCurrentTab(index); - break; - - case QWebEnginePage::WebDialog: - // a window without decorations - break; - - case QWebEnginePage::WebBrowserBackgroundTab: - // a web browser tab, but don't swap to it - break; - } - - return view; -} - void WebView::contextMenuEvent(QContextMenuEvent *event) { auto *menu = new WebViewContextMenu(this); diff --git a/src/webengine/webview.h b/src/webengine/webview.h index 5748691..a9c6866 100644 --- a/src/webengine/webview.h +++ b/src/webengine/webview.h @@ -11,10 +11,10 @@ #include "webpage.h" #include <QWebEngineView> +#include <functional> #include <session.hpp> class WebProfile; -class SubWindow; class WebViewContextMenu; class WebView final : public QWebEngineView { @@ -25,8 +25,10 @@ class WebView final : public QWebEngineView explicit WebView(QWidget *parent = nullptr); public: - explicit WebView(WebProfile *profile = nullptr, QWidget *parent = nullptr); - explicit WebView(const Session::WebView &data, QWidget *parent = nullptr); + typedef std::function<WebView *(QWebEnginePage::WebWindowType)> cb_createWindow_t; + + WebView(WebProfile *profile, cb_createWindow_t cb, QWidget *parent = nullptr); + WebView(const Session::WebView &data, cb_createWindow_t cb, QWidget *parent = nullptr); ~WebView() = default; [[nodiscard]] WebProfile *profile() const @@ -37,7 +39,10 @@ public: [[nodiscard]] Session::WebView serialize() const; - bool isLoaded() const; + bool isLoaded() const + { + return m_loaded; + } public slots: void search(const QString &term); @@ -46,11 +51,14 @@ signals: void newBookmark(const QString &title, const QUrl &url); protected: - WebView *createWindow(QWebEnginePage::WebWindowType type) override; + WebView *createWindow(QWebEnginePage::WebWindowType type) override + { + return cb_createWindow ? cb_createWindow(type) : nullptr; + } void contextMenuEvent(QContextMenuEvent *event) override; private: - SubWindow *m_parentWindow = nullptr; + cb_createWindow_t cb_createWindow; WebProfile *m_profile = nullptr; bool m_loaded = false; diff --git a/src/webengine/webviewcontextmenu.cpp b/src/webengine/webviewcontextmenu.cpp index 856ff9c..1f6b337 100644 --- a/src/webengine/webviewcontextmenu.cpp +++ b/src/webengine/webviewcontextmenu.cpp @@ -7,7 +7,6 @@ */ #include "webviewcontextmenu.h" -#include "wallet/wallet.h" #include "webprofilemanager.h" #include "webview.h" #include <QContextMenuEvent> @@ -216,6 +215,7 @@ WebViewContextMenu::WebViewContextMenu(WebView *view) } #ifndef NDEBUG + /* { this->addSeparator(); auto *autofillAction = this->addAction(tr("Autofill form")); @@ -223,5 +223,6 @@ WebViewContextMenu::WebViewContextMenu(WebView *view) Wallet::autocompleteForm(view); }); }; + */ #endif } |