aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAkshay Nair <phenax5@gmail.com>2025-07-27 14:54:56 +0530
committerAkshay Nair <phenax5@gmail.com>2025-07-27 14:54:56 +0530
commit9ece6a733f0c3bfd518ee14f520aed70124fac12 (patch)
tree54d045b187d39bf05c6f2653adb78bf2088f45ce
parentf13a1a1c4eab8bdef3760dd71cd7e2b3cc68a3e8 (diff)
downloadnull-browser-9ece6a733f0c3bfd518ee14f520aed70124fac12.tar.gz
null-browser-9ece6a733f0c3bfd518ee14f520aed70124fac12.zip
Add more tests for api + webview + webviewstack
-rw-r--r--TODO.org1
-rw-r--r--spec/BrowserWindowSpec.cpp79
-rw-r--r--spec/LuaRuntimeApiSpec.cpp272
-rw-r--r--spec/WebViewSpec.cpp100
-rw-r--r--src/LuaRuntime.cpp45
-rw-r--r--src/LuaRuntime.hpp50
-rw-r--r--src/LuaRuntimeApi.hpp1
-rw-r--r--src/schemes/NullRpcSchemeHandler.hpp14
-rw-r--r--src/widgets/BrowserApp.cpp5
-rw-r--r--src/widgets/BrowserWindow.cpp5
-rw-r--r--src/widgets/WebView.cpp13
-rw-r--r--src/widgets/WebView.hpp4
12 files changed, 495 insertions, 94 deletions
diff --git a/TODO.org b/TODO.org
index 3de1a7b..80f7dc6 100644
--- a/TODO.org
+++ b/TODO.org
@@ -13,6 +13,7 @@
- [ ] Generate docs for api
** Bugs
+- [ ] Managing focus in decorations?
- [ ] INVESTIGATE: Check why urlchanged doesnt fire for first url open sometimes
- [ ] INVESTIGATE: Segfault on close sometimes
- [ ] API's don't validate types. (type conversion segfaults)
diff --git a/spec/BrowserWindowSpec.cpp b/spec/BrowserWindowSpec.cpp
new file mode 100644
index 0000000..636b5c2
--- /dev/null
+++ b/spec/BrowserWindowSpec.cpp
@@ -0,0 +1,79 @@
+#include <QWebEngineNewWindowRequest>
+#include <QWebEngineProfile>
+#include <qwebengineprofile.h>
+#include <vector>
+
+#include "Configuration.hpp"
+#include "LuaRuntime.hpp"
+#include "testUtils.h"
+#include "widgets/Decorations.hpp"
+#include "widgets/WebView.hpp"
+#include "widgets/WebViewStack.hpp"
+
+// NOLINTBEGIN
+class BrowserWindowSpec : public QObject {
+ Q_OBJECT
+
+ // class FakeNewWindowRequest : public QWebEngineNewWindowRequest {
+ // public:
+ // FakeNewWindowRequest(DestinationType t, const QRect &r, const QUrl &u, bool b)
+ // : QWebEngineNewWindowRequest(t, r, u, b, nullptr) {}
+ // };
+
+ QWebEngineProfile *profile() { return new QWebEngineProfile(); }
+
+private slots:
+ void beforeTestCase() { LuaRuntime::instance().start_event_loop(); }
+ void cleanupTestCase() { LuaRuntime::instance().stop_event_loop(); }
+
+ void test_initial_state() {
+ describe("constructor");
+
+ context("when initialized with some urls");
+ it("opens those urls in stack") {
+ Configuration configuration;
+ QStringList urls{"https://example1.com", "https://example2.com"};
+
+ BrowserWindow browser_window(configuration, profile(), urls);
+
+ auto *webview_stack = browser_window.findChild<WebViewStack *>();
+ QCOMPARE(webview_stack->count(), 2);
+ std::vector<QUrl> expected_urls{QUrl("https://example1.com"), QUrl("https://example2.com")};
+ QCOMPARE(webview_stack->urls(), expected_urls);
+ }
+
+ context("when initialized with no urls");
+ it("opens configured new_view_url in stack") {
+ Configuration configuration;
+
+ BrowserWindow browser_window(configuration, profile());
+
+ auto *webview_stack = browser_window.findChild<WebViewStack *>();
+ QCOMPARE(webview_stack->count(), 1);
+ QCOMPARE(webview_stack->urls(), {QUrl(configuration.new_view_url())});
+ }
+ }
+
+ // void test_window_title_update() {
+ // context("when title is updated");
+ // it("opens configured new_view_url in stack") {
+ // Configuration configuration;
+ // BrowserWindow browser_window(configuration, profile(),
+ // {"https://one.com", "https://two.com"});
+ // auto *webview_stack = browser_window.findChild<WebViewStack *>();
+ // QSignalSpy current_webview_title_changed(webview_stack,
+ // &WebViewStack::current_webview_title_changed);
+ //
+ // emit webview_stack->current_webview_title_changed(0);
+ // QVERIFY(current_webview_title_changed.wait(100));
+ //
+ // qDebug() << browser_window.windowTitle();
+ // // QCOMPARE(webview_stack->count(), 1);
+ // // QCOMPARE(webview_stack->urls(), {QUrl(configuration.new_view_url())});
+ // }
+ // }
+};
+
+QTEST_REGISTER(BrowserWindowSpec)
+#include "BrowserWindowSpec.moc"
+// NOLINTEND
diff --git a/spec/LuaRuntimeApiSpec.cpp b/spec/LuaRuntimeApiSpec.cpp
index fe29fcc..123eaa1 100644
--- a/spec/LuaRuntimeApiSpec.cpp
+++ b/spec/LuaRuntimeApiSpec.cpp
@@ -1,10 +1,15 @@
#include <QtCore>
+#include <optional>
+#include <unordered_map>
+#include <utility>
#include <uv.h>
+#include <vector>
#include "LuaRuntime.hpp"
#include "WindowActionRouter.hpp"
#include "events/Event.hpp"
#include "testUtils.h"
+#include "widgets/WebView.hpp"
class TestEvent1 : public Event {
public:
@@ -33,16 +38,16 @@ private slots:
auto &lua = LuaRuntime::instance();
QSignalSpy evaluation_completed_spy(&lua, &LuaRuntime::evaluation_completed);
- lua.evaluate(R"(
+ lua.evaluate(R"LUA(
return web.event.add_listener({ 'Hello', 'World' }, {
patterns = { 'p1', 'p2' },
callback = function() print("Called") end,
});
- )");
+ )LUA");
evaluation_completed_spy.wait();
QCOMPARE(evaluation_completed_spy.count(), 1);
- QVariant result = evaluation_completed_spy.takeFirst().at(0);
+ QVariant result = evaluation_completed_spy.first()[0];
QCOMPARE(result, true);
}
@@ -51,16 +56,16 @@ private slots:
auto &lua = LuaRuntime::instance();
QSignalSpy evaluation_completed_spy(&lua, &LuaRuntime::evaluation_completed);
- lua.evaluate(R"(
+ lua.evaluate(R"LUA(
return web.event.add_listener('Hello', {
patterns = { 'p1', 'p2' },
callback = function() print("Called") end,
});
- )");
+ )LUA");
evaluation_completed_spy.wait();
QCOMPARE(evaluation_completed_spy.count(), 1);
- QVariant result = evaluation_completed_spy.takeFirst().at(0);
+ QVariant result = evaluation_completed_spy.takeFirst()[0];
QCOMPARE(result, true);
}
@@ -69,15 +74,15 @@ private slots:
auto &lua = LuaRuntime::instance();
QSignalSpy evaluation_completed_spy(&lua, &LuaRuntime::evaluation_completed);
- lua.evaluate(R"(
+ lua.evaluate(R"LUA(
return web.event.add_listener({ 'Hello', 'World' }, {
callback = function() print("Called") end,
});
- )");
+ )LUA");
evaluation_completed_spy.wait();
QCOMPARE(evaluation_completed_spy.count(), 1);
- QVariant result = evaluation_completed_spy.takeFirst().at(0);
+ QVariant result = evaluation_completed_spy.takeFirst()[0];
QCOMPARE(result, true);
}
@@ -86,12 +91,12 @@ private slots:
auto &lua = LuaRuntime::instance();
QSignalSpy evaluation_completed_spy(&lua, &LuaRuntime::evaluation_completed);
- lua.evaluate(R"(
+ lua.evaluate(R"LUA(
return web.event.add_listener(nil, {
patterns = { 'p1', 'p2' },
callback = function() print("Called") end,
});
- )");
+ )LUA");
evaluation_completed_spy.wait();
QCOMPARE(evaluation_completed_spy.count(), 1);
@@ -104,11 +109,11 @@ private slots:
auto &lua = LuaRuntime::instance();
QSignalSpy evaluation_completed_spy(&lua, &LuaRuntime::evaluation_completed);
- lua.evaluate(R"(
+ lua.evaluate(R"LUA(
return web.event.add_listener({'Ev'}, {
patterns = { 'p1', 'p2' },
});
- )");
+ )LUA");
evaluation_completed_spy.wait();
QCOMPARE(evaluation_completed_spy.count(), 1);
@@ -118,13 +123,13 @@ private slots:
}
void test_web_event_dispatching() {
- describe("web.event.add_listener (event dispatch)");
+ describe("web.event.add_listener (event dispatch)LUA");
- context("when dispatching a registered event (without pattern)");
+ context("when dispatching a registered event (without pattern)LUA");
it("calls the registered event handler") {
auto &lua = LuaRuntime::instance();
QSignalSpy evaluation_completed_spy(&lua, &LuaRuntime::evaluation_completed);
- lua.evaluate(R"(
+ lua.evaluate(R"LUA(
_G.event1_called = false;
_G.event1_called_with = nil;
web.event.add_listener('TestEvent1', {
@@ -137,7 +142,7 @@ private slots:
web.event.add_listener('TestEvent2', {
callback = function(opts) _G.event2_called = true end,
});
- )");
+ )LUA");
evaluation_completed_spy.wait();
TestEvent1 event(42);
@@ -149,17 +154,17 @@ private slots:
}
}
- void lua_api_view_set_url() {
- describe("web.search.set_url");
+ void test_lua_api_view_set_url() {
+ describe("web.view.set_url");
context("when called with a url and view id");
it("emits url_opened for the given view id") {
auto &lua = LuaRuntime::instance();
QSignalSpy url_opened(&lua, &LuaRuntime::url_opened);
- lua.evaluate(R"(
+ lua.evaluate(R"LUA(
web.view.set_url("https://updated-url.com", 42)
- )");
+ )LUA");
QVERIFY(url_opened.wait());
QCOMPARE(url_opened.first()[0], "https://updated-url.com");
@@ -172,9 +177,9 @@ private slots:
auto &lua = LuaRuntime::instance();
QSignalSpy url_opened(&lua, &LuaRuntime::url_opened);
- lua.evaluate(R"(
+ lua.evaluate(R"LUA(
web.view.set_url("https://updated-url.com")
- )");
+ )LUA");
QVERIFY(url_opened.wait());
QCOMPARE(url_opened.first()[0], "https://updated-url.com");
@@ -187,9 +192,9 @@ private slots:
auto &lua = LuaRuntime::instance();
QSignalSpy url_opened(&lua, &LuaRuntime::url_opened);
- lua.evaluate(R"(
+ lua.evaluate(R"LUA(
web.view.set_url()
- )");
+ )LUA");
QVERIFY(url_opened.wait());
QCOMPARE(url_opened.first()[0], "");
@@ -198,7 +203,7 @@ private slots:
}
}
- void lua_api_search_set_text() {
+ void test_lua_api_search_set_text() {
describe("web.search.set_text");
context("when called with just the search text");
@@ -226,7 +231,7 @@ private slots:
}
}
- void lua_api_search_next() {
+ void test_lua_api_search_next() {
describe("web.search.next");
context("when called without view id");
@@ -252,7 +257,7 @@ private slots:
}
}
- void lua_api_search_previous() {
+ void test_lua_api_search_previous() {
describe("web.search.previous");
context("when called without view id");
@@ -278,7 +283,7 @@ private slots:
}
}
- void lua_api_search_get_text() {
+ void test_lua_api_search_get_text() {
describe("web.search.get_text");
it("returns the current search text") {
@@ -294,7 +299,7 @@ private slots:
}
}
- void lua_api_view_open_devtools() {
+ void test_lua_api_view_open_devtools() {
describe("web.view.open_devtools");
context("when called with view id");
@@ -308,6 +313,213 @@ private slots:
QCOMPARE(devtools_requested.first().first(), 42);
}
}
+
+ void test_lua_api_view_expose() {
+ describe("web.view.expose");
+
+ context("when called without a view id");
+ it("defines the rpc for the current webview") {
+ auto &lua = LuaRuntime::instance();
+ QSignalSpy webview_rpc_action_defined(&lua, &LuaRuntime::webview_rpc_action_defined);
+
+ lua.evaluate(R"LUA(
+ web.view.expose('myfunction', function() end)
+ )LUA");
+
+ QVERIFY(webview_rpc_action_defined.wait());
+ QCOMPARE(webview_rpc_action_defined.first()[0], "myfunction");
+ auto func_raw = webview_rpc_action_defined.first()[1];
+ QVERIFY(func_raw.canConvert<RpcFunc>());
+ QCOMPARE(webview_rpc_action_defined.first()[2], 0);
+ }
+
+ context("when called with a view id");
+ it("defines the rpc for the given webview") {
+ auto &lua = LuaRuntime::instance();
+ QSignalSpy webview_rpc_action_defined(&lua, &LuaRuntime::webview_rpc_action_defined);
+
+ lua.evaluate(R"LUA(
+ web.view.expose('myfunction', function() end, { view = 5 })
+ )LUA");
+
+ QVERIFY(webview_rpc_action_defined.wait());
+ QCOMPARE(webview_rpc_action_defined.first()[0], "myfunction");
+ auto func_raw = webview_rpc_action_defined.first()[1];
+ QVERIFY(func_raw.canConvert<RpcFunc>());
+ QCOMPARE(webview_rpc_action_defined.first()[2], 5);
+ }
+
+ context("when the defined function is called with args");
+ it("calls the lua function with the given args") {
+ auto &lua = LuaRuntime::instance();
+ QSignalSpy webview_rpc_action_defined(&lua, &LuaRuntime::webview_rpc_action_defined);
+ lua.evaluate(R"LUA(
+ web.view.expose('myfunction', function(args)
+ _G.myfunction_was_called_with = args.prop
+ end, { view = 5 })
+ )LUA");
+ QVERIFY(webview_rpc_action_defined.wait());
+
+ auto func_raw = webview_rpc_action_defined.first()[1];
+ QVERIFY(func_raw.canConvert<RpcFunc>());
+ RpcArgs args{{"prop", "Test value"}};
+ func_raw.value<RpcFunc>()(args);
+
+ QVERIFY(wait_for_lua_to_be_true("return _G.myfunction_was_called_with == 'Test value'"));
+ }
+ }
+
+ void test_lua_api_view_set_html() {
+ describe("web.view.set_html");
+
+ context("when called without view id");
+ it("emits webview_html_set_requested for the given view id") {
+ auto &lua = LuaRuntime::instance();
+ QSignalSpy webview_html_set_requested(&lua, &LuaRuntime::webview_html_set_requested);
+
+ lua.evaluate(R"LUA(
+ web.view.set_html('<h1>foobar</h1>')
+ )LUA");
+
+ QVERIFY(webview_html_set_requested.wait());
+ QCOMPARE(webview_html_set_requested.first()[0], "<h1>foobar</h1>");
+ QCOMPARE(webview_html_set_requested.first()[1], 0);
+ }
+
+ context("when called with html and view id");
+ it("emits webview_html_set_requested for the given view id") {
+ auto &lua = LuaRuntime::instance();
+ QSignalSpy webview_html_set_requested(&lua, &LuaRuntime::webview_html_set_requested);
+
+ lua.evaluate(R"LUA(
+ web.view.set_html('<h1>foobar</h1>', { view = 42 })
+ )LUA");
+
+ QVERIFY(webview_html_set_requested.wait());
+ QCOMPARE(webview_html_set_requested.first()[0], "<h1>foobar</h1>");
+ QCOMPARE(webview_html_set_requested.first()[1], 42);
+ }
+ }
+
+ void test_lua_api_decorations_set_enabled() {
+ describe("web.decorations.*.enable");
+
+ context("when called without view id");
+ it("emits webview_html_set_requested without window id (nullopt)") {
+ auto &lua = LuaRuntime::instance();
+ QSignalSpy decorations_set_enabled(&lua, &LuaRuntime::decoration_set_enabled);
+
+ lua.evaluate(R"LUA(
+ web.decorations.left.enable()
+ )LUA");
+
+ QVERIFY(decorations_set_enabled.wait());
+ auto call = decorations_set_enabled.first();
+ QCOMPARE(call[0], 3);
+ QCOMPARE(call[1], true);
+ QCOMPARE(call[2].value<std::optional<int>>(), std::nullopt);
+ }
+
+ context("when called with window id");
+ it("emits webview_html_set_requested for the given window id") {
+ auto &lua = LuaRuntime::instance();
+ QSignalSpy decorations_set_enabled(&lua, &LuaRuntime::decoration_set_enabled);
+
+ lua.evaluate(R"LUA(
+ web.decorations.left.enable({ win = 42 })
+ )LUA");
+
+ QVERIFY(decorations_set_enabled.wait());
+ auto call = decorations_set_enabled.first();
+ QCOMPARE(call[0], 3);
+ QCOMPARE(call[1], true);
+ QCOMPARE(call[2].value<std::optional<WindowId>>(), std::make_optional(42));
+ }
+
+ context("for all decoration types");
+ it("emits webview_html_set_requested for the given window id") {
+ std::vector<std::pair<int, QString>> decoration_types{
+ {1, "top"},
+ {2, "bottom"},
+ {3, "left"},
+ {4, "right"},
+ };
+ for (auto &dir : decoration_types) {
+ auto &lua = LuaRuntime::instance();
+ QSignalSpy decorations_set_enabled(&lua, &LuaRuntime::decoration_set_enabled);
+
+ lua.evaluate(QString(R"LUA(
+ web.decorations.%1.enable({ win = 42 })
+ )LUA")
+ .arg(dir.second));
+
+ QVERIFY(decorations_set_enabled.wait());
+ auto call = decorations_set_enabled.first();
+ QCOMPARE(call[0], dir.first);
+ QCOMPARE(call[1], true);
+ QCOMPARE(call[2].value<std::optional<WindowId>>(), std::make_optional(42));
+ }
+ }
+
+ describe("web.decorations.*.disable");
+
+ context("when called without view id");
+ it("emits webview_html_set_requested without window id (nullopt)") {
+ auto &lua = LuaRuntime::instance();
+ QSignalSpy decorations_set_enabled(&lua, &LuaRuntime::decoration_set_enabled);
+
+ lua.evaluate(R"LUA(
+ web.decorations.left.disable()
+ )LUA");
+
+ QVERIFY(decorations_set_enabled.wait());
+ auto call = decorations_set_enabled.first();
+ QCOMPARE(call[0], 3);
+ QCOMPARE(call[1], false);
+ QCOMPARE(call[2].value<std::optional<int>>(), std::nullopt);
+ }
+
+ context("when called with window id");
+ it("emits webview_html_set_requested for the given window id") {
+ auto &lua = LuaRuntime::instance();
+ QSignalSpy decorations_set_enabled(&lua, &LuaRuntime::decoration_set_enabled);
+
+ lua.evaluate(R"LUA(
+ web.decorations.left.disable({ win = 42 })
+ )LUA");
+
+ QVERIFY(decorations_set_enabled.wait());
+ auto call = decorations_set_enabled.first();
+ QCOMPARE(call[0], 3);
+ QCOMPARE(call[1], false);
+ QCOMPARE(call[2].value<std::optional<WindowId>>(), std::make_optional(42));
+ }
+
+ context("for all decoration types");
+ it("emits webview_html_set_requested for the given window id") {
+ std::vector<std::pair<int, QString>> decoration_types{
+ {1, "top"},
+ {2, "bottom"},
+ {3, "left"},
+ {4, "right"},
+ };
+ for (auto &dir : decoration_types) {
+ auto &lua = LuaRuntime::instance();
+ QSignalSpy decorations_set_enabled(&lua, &LuaRuntime::decoration_set_enabled);
+
+ lua.evaluate(QString(R"LUA(
+ web.decorations.%1.disable({ win = 42 })
+ )LUA")
+ .arg(dir.second));
+
+ QVERIFY(decorations_set_enabled.wait());
+ auto call = decorations_set_enabled.first();
+ QCOMPARE(call[0], dir.first);
+ QCOMPARE(call[1], false);
+ QCOMPARE(call[2].value<std::optional<WindowId>>(), std::make_optional(42));
+ }
+ }
+ }
};
QTEST_REGISTER(LuaRuntimeApiSpec)
diff --git a/spec/WebViewSpec.cpp b/spec/WebViewSpec.cpp
new file mode 100644
index 0000000..3e67f11
--- /dev/null
+++ b/spec/WebViewSpec.cpp
@@ -0,0 +1,100 @@
+#include <QWebEngineNewWindowRequest>
+#include <QWebEngineProfile>
+#include <qdeadlinetimer.h>
+#include <qsignalspy.h>
+#include <qtestcase.h>
+#include <qtestsupport_core.h>
+#include <qurlquery.h>
+
+#include "LuaRuntime.hpp"
+#include "schemes/NullRpcSchemeHandler.hpp"
+#include "testUtils.h"
+#include "widgets/WebView.hpp"
+
+// NOLINTBEGIN
+class WebViewSpec : public QObject {
+ Q_OBJECT
+
+ QWebEngineProfile *profile() { return new QWebEngineProfile(); }
+
+private slots:
+ void beforeTestCase() { LuaRuntime::instance().start_event_loop(); }
+ void cleanupTestCase() { LuaRuntime::instance().stop_event_loop(); }
+
+ void test_rpc_enabled() {
+ describe("webview rpc enabled");
+
+ context("when a defined function is called");
+ it("calls the function") {
+ WebView webview(1, profile());
+ webview.enable_rpc_api();
+ bool my_func_was_called = false;
+ webview.expose_rpc_function(
+ "my_func", [&my_func_was_called](RpcArgs /* unused */) { my_func_was_called = true; });
+
+ auto &nullrpc = NullRPCSchemeHandler::instance();
+ NullRPCMessage message{.name = "my_func", .params = QUrlQuery()};
+ emit nullrpc.message_received(message);
+
+ QVERIFY(QTest::qWaitFor([&my_func_was_called]() { return my_func_was_called; }));
+ }
+
+ context("when a defined function is called with arguements");
+ it("calls the function with the arguments") {
+ WebView webview(1, profile());
+ webview.enable_rpc_api();
+ bool my_func_was_called = false;
+ QString my_func_arg = "";
+ webview.expose_rpc_function("my_func", [&my_func_was_called, &my_func_arg](RpcArgs args) {
+ my_func_was_called = true;
+ my_func_arg = args.contains("my_arg") ? args.at("my_arg").toString() : "";
+ });
+
+ auto &nullrpc = NullRPCSchemeHandler::instance();
+ NullRPCMessage message{.name = "my_func", .params = QUrlQuery{{"my_arg", "my arg value"}}};
+ emit nullrpc.message_received(message);
+
+ QVERIFY(QTest::qWaitFor([&my_func_was_called]() { return my_func_was_called; }));
+ QCOMPARE(my_func_arg, "my arg value");
+ }
+
+ context("when an undefined function is called");
+ it("does nothing") {
+ WebView webview(1, profile());
+ webview.enable_rpc_api();
+ bool my_func_was_called = false;
+ webview.expose_rpc_function(
+ "my_func", [&my_func_was_called](RpcArgs /* unused */) { my_func_was_called = true; });
+
+ auto &nullrpc = NullRPCSchemeHandler::instance();
+ NullRPCMessage message{.name = "my_undefined_func", .params = QUrlQuery()};
+ emit nullrpc.message_received(message);
+
+ QVERIFY(NOT QTest::qWaitFor([&my_func_was_called]() { return my_func_was_called; },
+ QDeadlineTimer(std::chrono::milliseconds(200))));
+ }
+ }
+
+ void test_rpc_disabled() {
+ describe("webview rpc disabled");
+
+ context("when a defined function is called");
+ it("does nothing") {
+ WebView webview(1, profile());
+ bool my_func_was_called = false;
+ webview.expose_rpc_function(
+ "my_func", [&my_func_was_called](RpcArgs /* unused */) { my_func_was_called = true; });
+
+ auto &nullrpc = NullRPCSchemeHandler::instance();
+ NullRPCMessage message{.name = "my_func", .params = QUrlQuery()};
+ emit nullrpc.message_received(message);
+
+ QVERIFY(NOT QTest::qWaitFor([&my_func_was_called]() { return my_func_was_called; },
+ QDeadlineTimer(std::chrono::milliseconds(200))));
+ }
+ }
+};
+
+QTEST_REGISTER(WebViewSpec)
+#include "WebViewSpec.moc"
+// NOLINTEND
diff --git a/src/LuaRuntime.cpp b/src/LuaRuntime.cpp
index e9dc86f..343266c 100644
--- a/src/LuaRuntime.cpp
+++ b/src/LuaRuntime.cpp
@@ -112,3 +112,48 @@ LuaRuntime::~LuaRuntime() {
lua_close(state);
state = nullptr;
}
+
+std::vector<QString> LuaRuntime::lua_tostringlist(lua_State *state) {
+ std::vector<QString> values;
+ if (!lua_istable(state, -1))
+ return values;
+
+ lua_pushnil(state); // First key for lua_next()
+ while (lua_next(state, -2) != 0) {
+ if (lua_isstring(state, -1))
+ values.emplace_back(lua_tostring(state, -1));
+ lua_pop(state, 1);
+ }
+ lua_pop(state, 1);
+
+ return values;
+}
+
+void LuaRuntime::inspect_lua_stack(lua_State *state) {
+ int top = lua_gettop(state);
+ qDebug() << "--- Lua Stack (top: " << top << ") ---\n";
+
+ for (int i = 1; i <= top; i++) {
+ int type = lua_type(state, i);
+ qDebug() << " " << i << ": " << lua_typename(state, type);
+ }
+
+ qDebug() << "---------------------------\n";
+ lua_settop(state, top);
+}
+
+QVariant LuaRuntime::get_lua_value(lua_State *state, int idx, QVariant default_value) {
+ if (lua_isnoneornil(state, idx))
+ return default_value;
+
+ if (lua_isstring(state, idx))
+ return lua_tostring(state, idx);
+
+ if (lua_isboolean(state, idx))
+ return lua_toboolean(state, idx);
+
+ if (lua_isnumber(state, idx))
+ return lua_tonumber(state, idx);
+
+ return lua_tostring(state, idx);
+}
diff --git a/src/LuaRuntime.hpp b/src/LuaRuntime.hpp
index 0e4584d..04c17bc 100644
--- a/src/LuaRuntime.hpp
+++ b/src/LuaRuntime.hpp
@@ -6,12 +6,10 @@
#include <optional>
#include "AsyncEventLoop.hpp"
-#include "lua.h"
#include "utils.hpp"
#include "widgets/BrowserWindow.hpp"
#include "widgets/Decorations.hpp"
#include "widgets/WebView.hpp"
-#include "widgets/WebViewStack.hpp"
#ifndef PROJECT_LUA_PATH
#define PROJECT_LUA_PATH ""
@@ -79,50 +77,8 @@ private:
lua_State *state;
AsyncEventLoop *event_loop = nullptr;
- // TEMP
public:
- static void inspect_lua_stack(lua_State *state) {
- int top = lua_gettop(state);
- qDebug() << "--- Lua Stack (top: " << top << ") ---\n";
-
- for (int i = 1; i <= top; i++) {
- int type = lua_type(state, i);
- qDebug() << " " << i << ": " << lua_typename(state, type);
- }
-
- qDebug() << "---------------------------\n";
- lua_settop(state, top);
- }
-
- static std::vector<QString> lua_tostringlist(lua_State *state) {
- std::vector<QString> values;
- if (!lua_istable(state, -1))
- return values;
-
- lua_pushnil(state); // First key for lua_next()
- while (lua_next(state, -2) != 0) {
- if (lua_isstring(state, -1))
- values.emplace_back(lua_tostring(state, -1));
- lua_pop(state, 1);
- }
- lua_pop(state, 1);
-
- return values;
- }
-
- static QVariant get_lua_value(lua_State *state, int idx, QVariant default_value = 0) {
- if (lua_isnoneornil(state, idx))
- return default_value;
-
- if (lua_isstring(state, idx))
- return lua_tostring(state, idx);
-
- if (lua_isboolean(state, idx))
- return lua_toboolean(state, idx);
-
- if (lua_isnumber(state, idx))
- return lua_tonumber(state, idx);
-
- return lua_tostring(state, idx);
- }
+ static void inspect_lua_stack(lua_State *state);
+ static std::vector<QString> lua_tostringlist(lua_State *state);
+ static QVariant get_lua_value(lua_State *state, int idx, QVariant default_value = 0);
};
diff --git a/src/LuaRuntimeApi.hpp b/src/LuaRuntimeApi.hpp
index 41d3364..2dcba15 100644
--- a/src/LuaRuntimeApi.hpp
+++ b/src/LuaRuntimeApi.hpp
@@ -6,7 +6,6 @@
#include "LuaRuntime.hpp"
#include "WindowActionRouter.hpp"
#include "events/Event.hpp"
-#include "lua.h"
#include "widgets/BrowserWindow.hpp"
#include "widgets/Decorations.hpp"
#include "widgets/WebView.hpp"
diff --git a/src/schemes/NullRpcSchemeHandler.hpp b/src/schemes/NullRpcSchemeHandler.hpp
index b4bb216..b2b22cb 100644
--- a/src/schemes/NullRpcSchemeHandler.hpp
+++ b/src/schemes/NullRpcSchemeHandler.hpp
@@ -5,6 +5,11 @@
#include <QtCore>
#include <qurlquery.h>
+struct NullRPCMessage {
+ QString name;
+ QUrlQuery params;
+};
+
class NullRPCSchemeHandler : public QWebEngineUrlSchemeHandler {
Q_OBJECT
@@ -30,8 +35,11 @@ public:
return;
}
- QUrlQuery query(url.query());
- emit message_received(url.host(), query);
+ NullRPCMessage message{
+ .name = url.host(),
+ .params = QUrlQuery(url.query()),
+ };
+ emit message_received(message);
// TODO: responses managed with request ids
QByteArray data = "{}";
@@ -42,7 +50,7 @@ public:
}
signals:
- void message_received(const QString &action, QUrlQuery params);
+ void message_received(NullRPCMessage message);
private:
NullRPCSchemeHandler() = default;
diff --git a/src/widgets/BrowserApp.cpp b/src/widgets/BrowserApp.cpp
index 922c5cc..d6f274b 100644
--- a/src/widgets/BrowserApp.cpp
+++ b/src/widgets/BrowserApp.cpp
@@ -41,6 +41,11 @@ BrowserApp::BrowserApp(Configuration &configuration) : configuration(configurati
setup_profile(profile);
}
+ // Default keymaps
+ auto &keymap = KeymapEvaluator::instance();
+ keymap.define_mode("n", {.passthrough = false});
+ keymap.define_mode("i", {.passthrough = true});
+
connect(&window_action_router, &WindowActionRouter::new_window_requested, this,
[this](const QUrl &url) { create_window({url.toString()}); });
};
diff --git a/src/widgets/BrowserWindow.cpp b/src/widgets/BrowserWindow.cpp
index 1fcf0c0..5d78360 100644
--- a/src/widgets/BrowserWindow.cpp
+++ b/src/widgets/BrowserWindow.cpp
@@ -37,11 +37,6 @@ BrowserWindow::BrowserWindow(const Configuration &configuration, QWebEngineProfi
}
}
- // Default keymaps
- auto &keymap = KeymapEvaluator::instance();
- keymap.define_mode("n", {.passthrough = false});
- keymap.define_mode("i", {.passthrough = true});
-
// Update window title when webview changes
connect(webview_stack, &WebViewStack::current_webview_title_changed, this, [this](int index) {
auto webviews = webview_stack->get_webview_list();
diff --git a/src/widgets/WebView.cpp b/src/widgets/WebView.cpp
index 604a502..2941e8d 100644
--- a/src/widgets/WebView.cpp
+++ b/src/widgets/WebView.cpp
@@ -5,8 +5,8 @@
#include <qwebenginepage.h>
#include <qwebengineprofile.h>
-#include "LuaRuntime.hpp"
#include "schemes/NullRpcSchemeHandler.hpp"
+
#include "widgets/WebView.hpp"
WebView::WebView(uint32_t webview_id, QWebEngineProfile *profile, QWidget *parent_node)
@@ -50,6 +50,7 @@ void WebView::scroll_to_bottom() {
}
void WebView::enable_rpc_api() {
+ rpc_enabled = true;
auto &nullrpc = NullRPCSchemeHandler::instance();
connect(&nullrpc, &NullRPCSchemeHandler::message_received, this, &WebView::on_rpc_message);
}
@@ -58,16 +59,14 @@ void WebView::expose_rpc_function(const QString &name, const RpcFunc &action) {
exposed_functions.insert({name, action});
}
-void WebView::on_rpc_message(const QString &action, const QUrlQuery &params) {
- if (!exposed_functions.contains(action)) {
- qDebug() << "function not defined:" << action;
+void WebView::on_rpc_message(const NullRPCMessage &message) {
+ if (!rpc_enabled || !exposed_functions.contains(message.name))
return;
- }
RpcArgs args;
- for (auto pair : params.queryItems())
+ for (auto pair : message.params.queryItems())
args.insert(pair);
- auto func = exposed_functions.at(action);
+ auto func = exposed_functions.at(message.name);
func(args);
}
diff --git a/src/widgets/WebView.hpp b/src/widgets/WebView.hpp
index 5be9831..7a767d3 100644
--- a/src/widgets/WebView.hpp
+++ b/src/widgets/WebView.hpp
@@ -12,6 +12,7 @@
#include <qurlquery.h>
#include <unordered_map>
+#include "schemes/NullRpcSchemeHandler.hpp"
#include "utils.hpp"
#include "widgets/DevtoolsWindow.hpp"
@@ -36,6 +37,7 @@ private:
uint32_t id;
DevtoolsWindow *devtools_window = nullptr;
std::unordered_map<QString, RpcFunc> exposed_functions;
+ bool rpc_enabled = false;
- void on_rpc_message(const QString &action, const QUrlQuery &params);
+ void on_rpc_message(const NullRPCMessage &message);
};