diff options
| author | Akshay Nair <phenax5@gmail.com> | 2025-07-27 14:54:56 +0530 |
|---|---|---|
| committer | Akshay Nair <phenax5@gmail.com> | 2025-07-27 14:54:56 +0530 |
| commit | 9ece6a733f0c3bfd518ee14f520aed70124fac12 (patch) | |
| tree | 54d045b187d39bf05c6f2653adb78bf2088f45ce | |
| parent | f13a1a1c4eab8bdef3760dd71cd7e2b3cc68a3e8 (diff) | |
| download | null-browser-9ece6a733f0c3bfd518ee14f520aed70124fac12.tar.gz null-browser-9ece6a733f0c3bfd518ee14f520aed70124fac12.zip | |
Add more tests for api + webview + webviewstack
| -rw-r--r-- | TODO.org | 1 | ||||
| -rw-r--r-- | spec/BrowserWindowSpec.cpp | 79 | ||||
| -rw-r--r-- | spec/LuaRuntimeApiSpec.cpp | 272 | ||||
| -rw-r--r-- | spec/WebViewSpec.cpp | 100 | ||||
| -rw-r--r-- | src/LuaRuntime.cpp | 45 | ||||
| -rw-r--r-- | src/LuaRuntime.hpp | 50 | ||||
| -rw-r--r-- | src/LuaRuntimeApi.hpp | 1 | ||||
| -rw-r--r-- | src/schemes/NullRpcSchemeHandler.hpp | 14 | ||||
| -rw-r--r-- | src/widgets/BrowserApp.cpp | 5 | ||||
| -rw-r--r-- | src/widgets/BrowserWindow.cpp | 5 | ||||
| -rw-r--r-- | src/widgets/WebView.cpp | 13 | ||||
| -rw-r--r-- | src/widgets/WebView.hpp | 4 |
12 files changed, 495 insertions, 94 deletions
@@ -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 ¶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 <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 ¶ms); + void on_rpc_message(const NullRPCMessage &message); }; |
