aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lua/null-browser/api.lua9
-rw-r--r--spec/LuaRuntimeApiSpec.cpp57
-rw-r--r--spec/LuaRuntimeSpec.cpp54
-rw-r--r--src/LuaRuntime.cpp37
-rw-r--r--src/LuaRuntime.hpp4
-rw-r--r--src/LuaRuntimeApi.hpp25
-rw-r--r--src/WindowActionRouter.cpp4
-rw-r--r--src/widgets/BrowserWindow.cpp5
-rw-r--r--src/widgets/BrowserWindow.hpp3
-rw-r--r--src/widgets/Decorations.cpp5
-rw-r--r--src/widgets/Decorations.hpp4
-rw-r--r--src/widgets/EdgeDecoration.cpp4
-rw-r--r--src/widgets/EdgeDecoration.hpp2
-rw-r--r--src/widgets/IWebViewMediator.hpp3
-rw-r--r--src/widgets/WebView.hpp3
-rw-r--r--src/widgets/WebViewStack.cpp5
-rw-r--r--src/widgets/WebViewStack.hpp3
17 files changed, 204 insertions, 23 deletions
diff --git a/lua/null-browser/api.lua b/lua/null-browser/api.lua
index 65a7663..e1f2acc 100644
--- a/lua/null-browser/api.lua
+++ b/lua/null-browser/api.lua
@@ -125,14 +125,15 @@ function web.view.set_url(url, view_id) return __internals.view_set_url(url, vie
--- @example
--- ```lua
--- web.view.set_html('<h2>HJello</h2>')
---- web.view.set_html('<h2>HJello</h2>', 3) -- Set html for view with id 3
+--- web.view.set_html('<h2>HJello</h2>', { view = 3 }) -- Set html for view with id 3
--- ```
function web.view.set_html(html, opts) return __internals.view_set_html(html, (opts or {}).view) end
--- @class RunJSOpts
--- @field view? number View id
+--- @field on_result fun(v: any): nil Callback when
---- Run js in a given view
+--- Run js in a view
---
--- @param js string HTML string
--- @param opts? RunJSOpts Options
@@ -140,9 +141,9 @@ function web.view.set_html(html, opts) return __internals.view_set_html(html, (o
--- @example
--- ```lua
--- web.view.run_js('console.log(42)')
---- web.view.run_js('console.log(42)', 3) -- Set html for view with id 3
+--- web.view.run_js('console.log(42)', { view = 3 }) -- Set html for view with id 3
--- ```
-function web.view.run_js(js, opts) return __internals.view_run_js(js, (opts or {}).view) end
+function web.view.run_js(js, opts) return __internals.view_run_js(js, opts or {}) end
--- @class ReloadOpts
--- @field view? number View id
diff --git a/spec/LuaRuntimeApiSpec.cpp b/spec/LuaRuntimeApiSpec.cpp
index 123eaa1..028ceb6 100644
--- a/spec/LuaRuntimeApiSpec.cpp
+++ b/spec/LuaRuntimeApiSpec.cpp
@@ -401,7 +401,7 @@ private slots:
}
}
- void test_lua_api_decorations_set_enabled() {
+ void test_lua_api_decorations_enable() {
describe("web.decorations.*.enable");
context("when called without view id");
@@ -460,7 +460,9 @@ private slots:
QCOMPARE(call[2].value<std::optional<WindowId>>(), std::make_optional(42));
}
}
+ }
+ void test_lua_api_decorations_disable() {
describe("web.decorations.*.disable");
context("when called without view id");
@@ -520,6 +522,59 @@ private slots:
}
}
}
+
+ void test_lua_api_view_run_js() {
+ describe("web.view.run_js");
+
+ context("when called without opts");
+ it("runs js in current view") {
+ auto &lua = LuaRuntime::instance();
+ QSignalSpy view_run_js(&lua, &LuaRuntime::webview_js_eval_requested);
+
+ lua.evaluate(R"LUA( web.view.run_js("dostuff(200)") )LUA");
+
+ QVERIFY(view_run_js.wait());
+ auto call = view_run_js.first();
+ QCOMPARE(call[0], "dostuff(200)");
+ QCOMPARE(call[1], 0);
+ QVERIFY(call[2].canConvert<JsOnResultFunc>()); // Noop function
+ }
+
+ context("when called with opts.view");
+ it("runs js in given view") {
+ auto &lua = LuaRuntime::instance();
+ QSignalSpy view_run_js(&lua, &LuaRuntime::webview_js_eval_requested);
+
+ lua.evaluate(R"LUA( web.view.run_js("dostuff(200)", { view = 42 }) )LUA");
+
+ QVERIFY(view_run_js.wait());
+ auto call = view_run_js.first();
+ QCOMPARE(call[0], "dostuff(200)");
+ QCOMPARE(call[1], 42);
+ QVERIFY(call[2].canConvert<JsOnResultFunc>());
+ }
+
+ context("when called with opts.on_result");
+ it("runs js in given view and calls on_result with the result") {
+ auto &lua = LuaRuntime::instance();
+ QSignalSpy view_run_js(&lua, &LuaRuntime::webview_js_eval_requested);
+
+ lua.evaluate(R"LUA(
+ _G.on_result_called_with = null
+ web.view.run_js("dostuff(200)", { view = 42, on_result = function(value)
+ _G.on_result_called_with = value
+ end })
+ )LUA");
+
+ QVERIFY(view_run_js.wait());
+ auto call = view_run_js.first();
+ QCOMPARE(call[0], "dostuff(200)");
+ QCOMPARE(call[1], 42);
+ QVERIFY(call[2].canConvert<JsOnResultFunc>());
+ call[2].value<JsOnResultFunc>()(9921.29);
+ QVERIFY(wait_for_lua_to_be_true(R"LUA( return _G.on_result_called_with == 9921.29 )LUA"));
+ }
+ }
};
QTEST_REGISTER(LuaRuntimeApiSpec)
diff --git a/spec/LuaRuntimeSpec.cpp b/spec/LuaRuntimeSpec.cpp
index aeaaab2..4d9270c 100644
--- a/spec/LuaRuntimeSpec.cpp
+++ b/spec/LuaRuntimeSpec.cpp
@@ -1,7 +1,9 @@
#include <QtCore>
+#include <qtestcase.h>
#include <uv.h>
#include "LuaRuntime.hpp"
+#include "lua.h"
#include "testUtils.h"
// NOLINTBEGIN
@@ -124,6 +126,58 @@ private slots:
QVERIFY(wait_for_lua_to_be_true("return _G.spawn_exit_code == 0"));
}
}
+
+ void test_push_qvariant() {
+ describe("LuaRuntime::push_qvariant");
+
+ context("when called with a QString");
+ it("pushes lua string") {
+ auto &lua = LuaRuntime::instance();
+ auto *state = lua.get_state();
+
+ LuaRuntime::push_qvariant(state, QString("hello"));
+
+ QVERIFY(lua_isstring(state, 1));
+ QCOMPARE(lua_tostring(state, 1), "hello");
+ lua_pop(state, 1);
+ }
+
+ context("when called with a QVariant int");
+ it("pushes lua number") {
+ auto &lua = LuaRuntime::instance();
+ auto *state = lua.get_state();
+
+ LuaRuntime::push_qvariant(state, QVariant(42));
+
+ QVERIFY(lua_isnumber(state, 1));
+ QCOMPARE(lua_tointeger(state, 1), 42);
+ lua_pop(state, 1);
+ }
+
+ context("when called with a QVariant double");
+ it("pushes lua number") {
+ auto &lua = LuaRuntime::instance();
+ auto *state = lua.get_state();
+
+ LuaRuntime::push_qvariant(state, QVariant(42.69));
+
+ QVERIFY(lua_isnumber(state, 1));
+ QCOMPARE(lua_tonumber(state, 1), 42.69);
+ lua_pop(state, 1);
+ }
+
+ context("when called with a QVariant bool");
+ it("pushes lua boolean") {
+ auto &lua = LuaRuntime::instance();
+ auto *state = lua.get_state();
+
+ LuaRuntime::push_qvariant(state, QVariant(true));
+
+ QVERIFY(lua_isboolean(state, 1));
+ QCOMPARE(lua_toboolean(state, 1), true);
+ lua_pop(state, 1);
+ }
+ }
};
QTEST_REGISTER(LuaRuntimeSpec)
diff --git a/src/LuaRuntime.cpp b/src/LuaRuntime.cpp
index 343266c..76dfb11 100644
--- a/src/LuaRuntime.cpp
+++ b/src/LuaRuntime.cpp
@@ -3,6 +3,7 @@
#include <cstdlib>
#include <cstring>
#include <lua.hpp>
+#include <optional>
#include <qvariant.h>
extern "C" {
#include <luv/luv.h>
@@ -157,3 +158,39 @@ QVariant LuaRuntime::get_lua_value(lua_State *state, int idx, QVariant default_v
return lua_tostring(state, idx);
}
+
+void LuaRuntime::push_qvariant(lua_State *state, std::optional<QVariant> opt) {
+ if (!opt.has_value()) {
+ lua_pushnil(state);
+ return;
+ }
+
+ auto type = QString(opt.value().typeName());
+ // qDebug() << type << opt.value();
+
+ // TODO: QVariantMap
+ // TODO: QVariantList
+
+ if (type == "") {
+ lua_pushnil(state);
+ return;
+ }
+
+ if (type == "bool") {
+ lua_pushboolean(state, opt.value().toBool());
+ return;
+ }
+
+ if (type == "int") {
+ lua_pushinteger(state, opt.value().toInt());
+ return;
+ }
+
+ if (type == "double") {
+ lua_pushnumber(state, opt.value().toDouble());
+ return;
+ }
+
+ // String default
+ lua_pushstring(state, opt.value().toString().toStdString().c_str());
+}
diff --git a/src/LuaRuntime.hpp b/src/LuaRuntime.hpp
index d5cd12c..78ae354 100644
--- a/src/LuaRuntime.hpp
+++ b/src/LuaRuntime.hpp
@@ -64,7 +64,8 @@ signals:
void webview_scroll_bottom_requested(WebViewId webview_id);
void decoration_set_enabled(DecorationType type, bool enabled, std::optional<WindowId> win_id);
void webview_html_set_requested(const QString &html, WebViewId view_id);
- void webview_js_eval_requested(const QString &js_code, WebViewId view_id);
+ void webview_js_eval_requested(const QString &js_code, WebViewId view_id,
+ const JsOnResultFunc &on_result);
void schedule_for_next_tick(const std::function<void()> &action);
void webview_rpc_action_defined(const QString &name, const RpcFunc &action, WebViewId view_id);
void webview_reload_requested(WebViewId webview_id);
@@ -83,4 +84,5 @@ public:
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);
+ static void push_qvariant(lua_State *state, std::optional<QVariant> opt);
};
diff --git a/src/LuaRuntimeApi.hpp b/src/LuaRuntimeApi.hpp
index 0af1e9e..d6a78d5 100644
--- a/src/LuaRuntimeApi.hpp
+++ b/src/LuaRuntimeApi.hpp
@@ -49,9 +49,30 @@ int lua_api_view_set_html(lua_State *state) {
int lua_api_view_run_js(lua_State *state) {
const char *js_code = lua_tostring(state, 1);
- WebViewId view_id = lua_isnoneornil(state, 2) ? 0 : lua_tointeger(state, 2);
+
+ lua_getfield(state, 2, "view");
+ WebViewId view_id = lua_isnoneornil(state, 3) ? 0 : lua_tointeger(state, 3);
+ lua_pop(state, 1);
+
+ lua_getfield(state, 2, "on_result");
+ JsOnResultFunc action = [](auto &) {};
+ if (lua_isfunction(state, 3)) {
+ lua_pushvalue(state, 3);
+ const int function_ref = luaL_ref(state, LUA_REGISTRYINDEX);
+ action = [state, function_ref](const std::optional<QVariant> &value) {
+ preserve_top(state, {
+ lua_rawgeti(state, LUA_REGISTRYINDEX, function_ref);
+ LuaRuntime::push_qvariant(state, value);
+ lua_pcall(state, 1, 0, 0);
+ luaL_unref(state, LUA_REGISTRYINDEX, function_ref);
+ })
+ };
+ }
+ lua_pop(state, 1);
+
auto &runtime = LuaRuntime::instance();
- emit runtime.webview_js_eval_requested(js_code, view_id);
+ emit runtime.webview_js_eval_requested(js_code, view_id, action);
+
return 1;
}
diff --git a/src/WindowActionRouter.cpp b/src/WindowActionRouter.cpp
index 558a46e..f513210 100644
--- a/src/WindowActionRouter.cpp
+++ b/src/WindowActionRouter.cpp
@@ -78,9 +78,9 @@ void WindowActionRouter::initialize(Configuration *config) {
{ window->expose_rpc_function(name, action, webview_id); });
});
connect(&runtime, &LuaRuntime::webview_js_eval_requested, this,
- [this](const QString &js_code, WebViewId webview_id) {
+ [this](const QString &js_code, WebViewId webview_id, const JsOnResultFunc &on_result) {
WITH_WEBVIEW_WINDOW(webview_id, window,
- { window->run_javascript(js_code, webview_id); });
+ { window->run_javascript(js_code, webview_id, on_result); });
});
connect(&runtime, &LuaRuntime::webview_reload_requested, this, [this](WebViewId webview_id) {
WITH_WEBVIEW_WINDOW(webview_id, window, { window->reload(webview_id); });
diff --git a/src/widgets/BrowserWindow.cpp b/src/widgets/BrowserWindow.cpp
index 87e315c..acb07f7 100644
--- a/src/widgets/BrowserWindow.cpp
+++ b/src/widgets/BrowserWindow.cpp
@@ -102,8 +102,9 @@ void BrowserWindow::set_html(const QString &html, WebViewId webview_id) {
get_webview_mediator(webview_id)->set_html(html, webview_id);
}
-void BrowserWindow::run_javascript(const QString &js_code, WebViewId webview_id) {
- get_webview_mediator(webview_id)->run_javascript(js_code, webview_id);
+void BrowserWindow::run_javascript(const QString &js_code, WebViewId webview_id,
+ const JsOnResultFunc &on_result) {
+ get_webview_mediator(webview_id)->run_javascript(js_code, webview_id, on_result);
}
void BrowserWindow::expose_rpc_function(const QString &name, const RpcFunc &action,
diff --git a/src/widgets/BrowserWindow.hpp b/src/widgets/BrowserWindow.hpp
index de48c6e..2842983 100644
--- a/src/widgets/BrowserWindow.hpp
+++ b/src/widgets/BrowserWindow.hpp
@@ -38,7 +38,8 @@ public:
bool has_webview(WebViewId webview_id);
void open_url(const QUrl &url, OpenType open_type, WebViewId webview_id);
void set_html(const QString &html, WebViewId webview_id);
- void run_javascript(const QString &js_code, WebViewId webview_id);
+ void run_javascript(const QString &js_code, WebViewId webview_id,
+ const JsOnResultFunc &on_result);
void expose_rpc_function(const QString &name, const RpcFunc &action, WebViewId webview_id);
void reload(WebViewId webview_id);
diff --git a/src/widgets/Decorations.cpp b/src/widgets/Decorations.cpp
index 1cff3d4..22aa9f9 100644
--- a/src/widgets/Decorations.cpp
+++ b/src/widgets/Decorations.cpp
@@ -84,10 +84,11 @@ void Decorations::set_html(const QString &html, WebViewId view_id) {
decoration.value()->set_html(html);
}
-void Decorations::run_javascript(const QString &js_code, WebViewId view_id) {
+void Decorations::run_javascript(const QString &js_code, WebViewId view_id,
+ const JsOnResultFunc &on_result) {
auto decoration = get_decoration_widget_by_view_id(view_id);
if (decoration.has_value())
- decoration.value()->run_javascript(js_code);
+ decoration.value()->run_javascript(js_code, on_result);
}
std::optional<WebViewId> Decorations::get_view_id(DecorationType type) {
diff --git a/src/widgets/Decorations.hpp b/src/widgets/Decorations.hpp
index 108b13e..3df4fd7 100644
--- a/src/widgets/Decorations.hpp
+++ b/src/widgets/Decorations.hpp
@@ -3,6 +3,7 @@
#include "WebViewData.hpp"
#include "widgets/EdgeDecoration.hpp"
#include "widgets/IWebViewMediator.hpp"
+#include "widgets/WebView.hpp"
#include <QWidget>
#include <QtCore>
#include <optional>
@@ -29,7 +30,8 @@ public:
bool has_webview(WebViewId view_id) override;
void open_url(const QUrl &url, OpenType open_type, WebViewId view_id) override;
void set_html(const QString &html, WebViewId view_id) override;
- void run_javascript(const QString &js_code, WebViewId webview_id) override;
+ void run_javascript(const QString &js_code, WebViewId webview_id,
+ const JsOnResultFunc &on_result) override;
void expose_rpc_function(const QString &name, const RpcFunc &action,
WebViewId webview_id) override;
void reload(WebViewId webview_id) override;
diff --git a/src/widgets/EdgeDecoration.cpp b/src/widgets/EdgeDecoration.cpp
index bb114bf..76974be 100644
--- a/src/widgets/EdgeDecoration.cpp
+++ b/src/widgets/EdgeDecoration.cpp
@@ -94,10 +94,10 @@ void EdgeDecoration::set_url(const QUrl &url) {
webview.value()->setUrl(url);
}
-void EdgeDecoration::run_javascript(const QString &js_code) {
+void EdgeDecoration::run_javascript(const QString &js_code, const JsOnResultFunc &on_result) {
if (!webview.has_value())
return;
- webview.value()->run_javascript(js_code);
+ webview.value()->run_javascript(js_code, on_result);
}
std::optional<WebViewId> EdgeDecoration::get_view_id() {
diff --git a/src/widgets/EdgeDecoration.hpp b/src/widgets/EdgeDecoration.hpp
index 2c92de7..ed64658 100644
--- a/src/widgets/EdgeDecoration.hpp
+++ b/src/widgets/EdgeDecoration.hpp
@@ -21,7 +21,7 @@ public:
void set_html(const QString &content);
void set_enabled(bool enabled_value);
void set_url(const QUrl &url);
- void run_javascript(const QString &js_code);
+ void run_javascript(const QString &js_code, const JsOnResultFunc &on_result);
std::optional<WebViewId> get_view_id();
void expose_rpc_function(const QString &name, const RpcFunc &action);
diff --git a/src/widgets/IWebViewMediator.hpp b/src/widgets/IWebViewMediator.hpp
index a7007f2..a5a8fdb 100644
--- a/src/widgets/IWebViewMediator.hpp
+++ b/src/widgets/IWebViewMediator.hpp
@@ -12,6 +12,7 @@ public:
virtual void set_html(const QString &html, WebViewId webview_id) = 0;
virtual void expose_rpc_function(const QString &name, const RpcFunc &action,
WebViewId webview_id) = 0;
- virtual void run_javascript(const QString &js_code, WebViewId webview_id) = 0;
+ virtual void run_javascript(const QString &js_code, WebViewId webview_id,
+ const JsOnResultFunc &on_result) = 0;
virtual void reload(WebViewId webview_id) = 0;
};
diff --git a/src/widgets/WebView.hpp b/src/widgets/WebView.hpp
index 36a252d..0f2bd43 100644
--- a/src/widgets/WebView.hpp
+++ b/src/widgets/WebView.hpp
@@ -20,6 +20,8 @@
using RpcArgs = std::unordered_map<QString, QVariant>;
using RpcFunc = std::function<void(RpcArgs)>;
+using JsOnResultFunc = std::function<void(const std::optional<QVariant> &value)>;
+
class WebView : public QWebEngineView {
Q_OBJECT
@@ -31,6 +33,7 @@ public:
void scroll_to_bottom();
void enable_rpc_api();
void expose_rpc_function(const QString &name, const RpcFunc &action);
+ // void run_javascript(const QString &code, const JsOnResultFunc &on_result);
DELEGATE(page(), runJavaScript, run_javascript)
DEFINE_GETTER(get_id, id)
diff --git a/src/widgets/WebViewStack.cpp b/src/widgets/WebViewStack.cpp
index ab5808f..5503a3c 100644
--- a/src/widgets/WebViewStack.cpp
+++ b/src/widgets/WebViewStack.cpp
@@ -342,14 +342,15 @@ void WebViewStack::set_html(const QString &html, WebViewId webview_id) {
webview->setHtml(html);
}
-void WebViewStack::run_javascript(const QString &js_code, WebViewId webview_id) {
+void WebViewStack::run_javascript(const QString &js_code, WebViewId webview_id,
+ const JsOnResultFunc &on_result) {
auto *webview = get_webview(webview_id);
if (webview == nullptr) {
qDebug() << "Webview does not exist";
return;
}
- webview->run_javascript(js_code);
+ webview->run_javascript(js_code, on_result);
}
void WebViewStack::expose_rpc_function(const QString &name, const RpcFunc & /* unused */,
diff --git a/src/widgets/WebViewStack.hpp b/src/widgets/WebViewStack.hpp
index bbee44c..cbff0be 100644
--- a/src/widgets/WebViewStack.hpp
+++ b/src/widgets/WebViewStack.hpp
@@ -62,7 +62,8 @@ public slots:
void scroll_to_top(WebViewId webview_id);
void scroll_to_bottom(WebViewId webview_id);
void set_html(const QString &html, WebViewId webview_id = 0) override;
- void run_javascript(const QString &js_code, WebViewId webview_id) override;
+ void run_javascript(const QString &js_code, WebViewId webview_id,
+ const JsOnResultFunc &on_result) override;
void expose_rpc_function(const QString &name, const RpcFunc &action,
WebViewId webview_id) override;
void reload(WebViewId webview_id) override;