From 9ece6a733f0c3bfd518ee14f520aed70124fac12 Mon Sep 17 00:00:00 2001 From: Akshay Nair Date: Sun, 27 Jul 2025 14:54:56 +0530 Subject: Add more tests for api + webview + webviewstack --- TODO.org | 1 + spec/BrowserWindowSpec.cpp | 79 ++++++++++ spec/LuaRuntimeApiSpec.cpp | 272 +++++++++++++++++++++++++++++++---- spec/WebViewSpec.cpp | 100 +++++++++++++ src/LuaRuntime.cpp | 45 ++++++ src/LuaRuntime.hpp | 50 +------ src/LuaRuntimeApi.hpp | 1 - src/schemes/NullRpcSchemeHandler.hpp | 14 +- src/widgets/BrowserApp.cpp | 5 + src/widgets/BrowserWindow.cpp | 5 - src/widgets/WebView.cpp | 13 +- src/widgets/WebView.hpp | 4 +- 12 files changed, 495 insertions(+), 94 deletions(-) create mode 100644 spec/BrowserWindowSpec.cpp create mode 100644 spec/WebViewSpec.cpp 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 +#include +#include +#include + +#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(); + QCOMPARE(webview_stack->count(), 2); + std::vector 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(); + 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(); + // 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 +#include +#include +#include #include +#include #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()); + 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()); + 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()); + RpcArgs args{{"prop", "Test value"}}; + func_raw.value()(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('

foobar

') + )LUA"); + + QVERIFY(webview_html_set_requested.wait()); + QCOMPARE(webview_html_set_requested.first()[0], "

foobar

"); + 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('

foobar

', { view = 42 }) + )LUA"); + + QVERIFY(webview_html_set_requested.wait()); + QCOMPARE(webview_html_set_requested.first()[0], "

foobar

"); + 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::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::make_optional(42)); + } + + context("for all decoration types"); + it("emits webview_html_set_requested for the given window id") { + std::vector> 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::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::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::make_optional(42)); + } + + context("for all decoration types"); + it("emits webview_html_set_requested for the given window id") { + std::vector> 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::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 +#include +#include +#include +#include +#include +#include + +#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 LuaRuntime::lua_tostringlist(lua_State *state) { + std::vector 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 #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 lua_tostringlist(lua_State *state) { - std::vector 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 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 #include +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 #include -#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 ¶ms) { - 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 #include +#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 exposed_functions; + bool rpc_enabled = false; - void on_rpc_message(const QString &action, const QUrlQuery ¶ms); + void on_rpc_message(const NullRPCMessage &message); }; -- cgit v1.3.1