aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAkshay Nair <phenax5@gmail.com>2025-07-27 20:21:07 +0530
committerAkshay Nair <phenax5@gmail.com>2025-07-27 20:27:42 +0530
commit8604765c3660dbea9cae6996d5750bfb985e6751 (patch)
tree3a90f68dd07f8eb6a9c92d1f322162ac3b345383
parentc95ee83c24b79e64a91a6d3a55addcb6e7ebd11b (diff)
downloadnull-browser-8604765c3660dbea9cae6996d5750bfb985e6751.tar.gz
null-browser-8604765c3660dbea9cae6996d5750bfb985e6751.zip
Add web.view.run_js + change rpc js api
-rw-r--r--TODO.org4
-rw-r--r--lua/null-browser/api.lua17
-rw-r--r--lua/null-browser/extras/tabline.lua2
-rw-r--r--src/LuaRuntime.hpp1
-rw-r--r--src/LuaRuntimeApi.hpp9
-rw-r--r--src/WindowActionRouter.cpp5
-rw-r--r--src/WindowActionRouter.hpp1
-rw-r--r--src/widgets/BrowserWindow.cpp4
-rw-r--r--src/widgets/BrowserWindow.hpp1
-rw-r--r--src/widgets/Decorations.cpp6
-rw-r--r--src/widgets/Decorations.hpp1
-rw-r--r--src/widgets/EdgeDecoration.cpp9
-rw-r--r--src/widgets/EdgeDecoration.hpp1
-rw-r--r--src/widgets/IWebViewMediator.hpp1
-rw-r--r--src/widgets/WebView.cpp29
-rw-r--r--src/widgets/WebView.hpp2
-rw-r--r--src/widgets/WebViewStack.cpp10
-rw-r--r--src/widgets/WebViewStack.hpp1
18 files changed, 85 insertions, 19 deletions
diff --git a/TODO.org b/TODO.org
index 80f7dc6..ad7c50b 100644
--- a/TODO.org
+++ b/TODO.org
@@ -3,7 +3,7 @@
- [X] web.schedule
- [X] Create view with html (web.view.set_html())
- [X] Expose JS <-> lua in page (web.view.expose())
-- [-] Tests for api
+- [X] Tests for api
- [ ] Run JS in page (web.view.eval_js())
- [ ] web.decorations.*.set_size()
- [ ] f-key navigation
@@ -117,7 +117,7 @@ web.event.add_listener({ 'TabOpen', 'TabClose' }, {
local views = web.view.list()
for index, view in ipairs(web.view.list()) do
local text = index..': '..view.title..' ('..view.url..')'
- local tab = '<span onclick="__nullbrowser__.openTab('..view.id..')">' .. text .. '</span>')
+ local tab = '<span onclick="_nullbrowser.rpc.openTab('..view.id..')">' .. text .. '</span>')
tabs_html = tabs_html .. tab
end
web.view.set_html(tabs_html, { view = web.decorations.top.view() })
diff --git a/lua/null-browser/api.lua b/lua/null-browser/api.lua
index 07b7072..5475beb 100644
--- a/lua/null-browser/api.lua
+++ b/lua/null-browser/api.lua
@@ -129,6 +129,21 @@ function web.view.set_url(url, view_id) return __internals.view_set_url(url, vie
--- ```
function web.view.set_html(html, opts) return __internals.view_set_html(html, (opts or {}).view) end
+--- @class RunJSOpts
+--- @field view? number View id
+
+--- Set html in a given view
+---
+--- @param js string HTML string
+--- @param opts? RunJSOpts Options
+---
+--- @example
+--- ```lua
+--- web.view.run_js('console.log(42)')
+--- web.view.run_js('console.log(42)', 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
+
--- @class ExposeOpts
--- @field view? number View id
@@ -146,7 +161,7 @@ function web.view.set_html(html, opts) return __internals.view_set_html(html, (o
--- ```
--- then in js
--- ```js
---- __nullbrowser.tab_select({ view: 5 })
+--- _nullbrowser.rpc.tab_select({ view: 5 })
--- ```
function web.view.expose(name, action, opts) return __internals.view_expose(name, action, (opts or {}).view) end
diff --git a/lua/null-browser/extras/tabline.lua b/lua/null-browser/extras/tabline.lua
index b9156f5..e265940 100644
--- a/lua/null-browser/extras/tabline.lua
+++ b/lua/null-browser/extras/tabline.lua
@@ -47,7 +47,7 @@ function tabline.tabs_html()
for index, view in ipairs(web.view.list()) do
local tab = html.button({
class = 'tab' .. (web.view.current() == view.id and ' current' or ''),
- onclick = '__nullbrowser.tab_select({ view: ' .. view.id .. ' })',
+ onclick = '_nullbrowser.rpc.tab_select({ view: ' .. view.id .. ' })',
}, {
index .. ': ' .. view.title .. ' (' .. view.url .. ')',
})
diff --git a/src/LuaRuntime.hpp b/src/LuaRuntime.hpp
index 04c17bc..66572e4 100644
--- a/src/LuaRuntime.hpp
+++ b/src/LuaRuntime.hpp
@@ -64,6 +64,7 @@ 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 schedule_for_next_tick(const std::function<void()> &action);
void webview_rpc_action_defined(const QString &name, const RpcFunc &action, WebViewId view_id);
diff --git a/src/LuaRuntimeApi.hpp b/src/LuaRuntimeApi.hpp
index 2dcba15..28ffd5f 100644
--- a/src/LuaRuntimeApi.hpp
+++ b/src/LuaRuntimeApi.hpp
@@ -47,6 +47,14 @@ int lua_api_view_set_html(lua_State *state) {
return 1;
}
+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);
+ auto &runtime = LuaRuntime::instance();
+ emit runtime.webview_js_eval_requested(js_code, view_id);
+ return 1;
+}
+
int lua_api_view_create(lua_State *state) {
const char *url = luaL_optstring(state, 1, "");
auto &runtime = LuaRuntime::instance();
@@ -377,6 +385,7 @@ static luaL_Reg internals_api[] = {
luaL_Reg{"view_select", &lua_view_select},
luaL_Reg{"view_set_url", &lua_api_view_set_url},
luaL_Reg{"view_set_html", &lua_api_view_set_html},
+ luaL_Reg{"view_run_js", &lua_api_view_run_js},
luaL_Reg{"view_open_devtools", &lua_api_view_open_devtools},
luaL_Reg{"view_scroll", &lua_api_view_scroll},
luaL_Reg{"view_scroll_to_top", &lua_api_view_scroll_top},
diff --git a/src/WindowActionRouter.cpp b/src/WindowActionRouter.cpp
index d0cc6d0..3db1cb3 100644
--- a/src/WindowActionRouter.cpp
+++ b/src/WindowActionRouter.cpp
@@ -77,6 +77,11 @@ void WindowActionRouter::initialize(Configuration *config) {
WITH_WEBVIEW_WINDOW(webview_id, window,
{ window->expose_rpc_function(name, action, webview_id); });
});
+ connect(&runtime, &LuaRuntime::webview_js_eval_requested, this,
+ [this](const QString &js_code, WebViewId webview_id) {
+ WITH_WEBVIEW_WINDOW(webview_id, window,
+ { window->run_javascript(js_code, webview_id); });
+ });
// Search
connect(&runtime, &LuaRuntime::search_requested, this,
diff --git a/src/WindowActionRouter.hpp b/src/WindowActionRouter.hpp
index cec6224..efc54ca 100644
--- a/src/WindowActionRouter.hpp
+++ b/src/WindowActionRouter.hpp
@@ -13,7 +13,6 @@
#include "utils.hpp"
#include "widgets/BrowserWindow.hpp"
#include "widgets/Decorations.hpp"
-#include "widgets/WebViewStack.hpp"
#define WITH_WEBVIEW_WINDOW(WEBVIEW_ID, IDENT, BLOCK) \
for (auto &win_match : window_map) { \
diff --git a/src/widgets/BrowserWindow.cpp b/src/widgets/BrowserWindow.cpp
index 5d78360..1971b9c 100644
--- a/src/widgets/BrowserWindow.cpp
+++ b/src/widgets/BrowserWindow.cpp
@@ -93,6 +93,10 @@ 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::expose_rpc_function(const QString &name, const RpcFunc &action,
WebViewId webview_id) {
get_webview_mediator(webview_id)->expose_rpc_function(name, action, webview_id);
diff --git a/src/widgets/BrowserWindow.hpp b/src/widgets/BrowserWindow.hpp
index 3dc9e69..2ea7b88 100644
--- a/src/widgets/BrowserWindow.hpp
+++ b/src/widgets/BrowserWindow.hpp
@@ -38,6 +38,7 @@ 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 expose_rpc_function(const QString &name, const RpcFunc &action, WebViewId webview_id);
bool on_window_key_event(QKeyEvent *event);
diff --git a/src/widgets/Decorations.cpp b/src/widgets/Decorations.cpp
index 7886442..dc644d5 100644
--- a/src/widgets/Decorations.cpp
+++ b/src/widgets/Decorations.cpp
@@ -84,6 +84,12 @@ 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) {
+ auto decoration = get_decoration_widget_by_view_id(view_id);
+ if (decoration.has_value())
+ decoration.value()->run_javascript(js_code);
+}
+
std::optional<WebViewId> Decorations::get_view_id(DecorationType type) {
auto decoration = get_decoration_widget_type(type);
return decoration.has_value() ? decoration.value()->get_view_id() : std::nullopt;
diff --git a/src/widgets/Decorations.hpp b/src/widgets/Decorations.hpp
index 9788359..97e0415 100644
--- a/src/widgets/Decorations.hpp
+++ b/src/widgets/Decorations.hpp
@@ -29,6 +29,7 @@ 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 expose_rpc_function(const QString &name, const RpcFunc &action,
WebViewId webview_id) override;
diff --git a/src/widgets/EdgeDecoration.cpp b/src/widgets/EdgeDecoration.cpp
index 7351552..bb114bf 100644
--- a/src/widgets/EdgeDecoration.cpp
+++ b/src/widgets/EdgeDecoration.cpp
@@ -11,7 +11,8 @@
QString default_html_layout = R"HTML(
<script>
- window.__nullbrowser ||= (() => {
+ window._nullbrowser ||= {};
+ window._nullbrowser.rpc ||= (() => {
const invoke = (action, opts = {}) => {
const urlParams = new URLSearchParams(opts);
const url = `nullrpc://${action}?${urlParams.toString()}`;
@@ -93,6 +94,12 @@ void EdgeDecoration::set_url(const QUrl &url) {
webview.value()->setUrl(url);
}
+void EdgeDecoration::run_javascript(const QString &js_code) {
+ if (!webview.has_value())
+ return;
+ webview.value()->run_javascript(js_code);
+}
+
std::optional<WebViewId> EdgeDecoration::get_view_id() {
return webview.has_value() ? std::make_optional(webview.value()->get_id()) : std::nullopt;
}
diff --git a/src/widgets/EdgeDecoration.hpp b/src/widgets/EdgeDecoration.hpp
index 3c90cfb..2c92de7 100644
--- a/src/widgets/EdgeDecoration.hpp
+++ b/src/widgets/EdgeDecoration.hpp
@@ -21,6 +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);
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 b9af36f..9c64e53 100644
--- a/src/widgets/IWebViewMediator.hpp
+++ b/src/widgets/IWebViewMediator.hpp
@@ -12,4 +12,5 @@ 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;
};
diff --git a/src/widgets/WebView.cpp b/src/widgets/WebView.cpp
index 2941e8d..91615a4 100644
--- a/src/widgets/WebView.cpp
+++ b/src/widgets/WebView.cpp
@@ -28,25 +28,28 @@ void WebView::open_devtools() {
}
void WebView::scroll_increment(int deltax, int deltay) {
- auto code = QString(R"JS((() => {
- const $el = document.scrollingElement;
- $el.scrollTo($el.scrollLeft + %1, $el.scrollTop + %2);
- })())JS")
- .arg(deltax)
- .arg(deltay);
-
- page()->runJavaScript(code);
+ // clang-format off
+ run_javascript(
+ QString(R"JS(
+ (() => {
+ const $el = document.scrollingElement;
+ $el.scrollTo($el.scrollLeft + %1, $el.scrollTop + %2);
+ })()
+ )JS").arg(deltax).arg(deltay)
+ );
+ // clang-format on
}
void WebView::scroll_to_top() {
- auto code = QString(R"JS(document.scrollingElement.scrollTo(0, 0))JS");
- page()->runJavaScript(code);
+ run_javascript(R"JS(
+ document.scrollingElement.scrollTo(0, 0)
+ )JS");
}
void WebView::scroll_to_bottom() {
- auto code = QString(
- R"JS(document.scrollingElement.scrollTo(0, document.scrollingElement.scrollHeight))JS");
- page()->runJavaScript(code);
+ run_javascript(R"JS(
+ document.scrollingElement.scrollTo(0, document.scrollingElement.scrollHeight)
+ )JS");
}
void WebView::enable_rpc_api() {
diff --git a/src/widgets/WebView.hpp b/src/widgets/WebView.hpp
index 7a767d3..36a252d 100644
--- a/src/widgets/WebView.hpp
+++ b/src/widgets/WebView.hpp
@@ -1,6 +1,7 @@
#pragma once
#include <QMainWindow>
+#include <QWebEnginePage>
#include <QWebEngineUrlRequestInterceptor>
#include <QWebEngineUrlRequestJob>
#include <QWebEngineUrlSchemeHandler>
@@ -31,6 +32,7 @@ public:
void enable_rpc_api();
void expose_rpc_function(const QString &name, const RpcFunc &action);
+ DELEGATE(page(), runJavaScript, run_javascript)
DEFINE_GETTER(get_id, id)
private:
diff --git a/src/widgets/WebViewStack.cpp b/src/widgets/WebViewStack.cpp
index 0ed2399..37532ff 100644
--- a/src/widgets/WebViewStack.cpp
+++ b/src/widgets/WebViewStack.cpp
@@ -342,6 +342,16 @@ void WebViewStack::set_html(const QString &html, WebViewId webview_id) {
webview->setHtml(html);
}
+void WebViewStack::run_javascript(const QString &js_code, WebViewId webview_id) {
+ auto *webview = get_webview(webview_id);
+ if (webview == nullptr) {
+ qDebug() << "Webview does not exist";
+ return;
+ }
+
+ webview->run_javascript(js_code);
+}
+
void WebViewStack::expose_rpc_function(const QString &name, const RpcFunc & /* unused */,
WebViewId /* unused */) {
qDebug() << "expose_rpc_function: NOT IMPLEMENTED" << name;
diff --git a/src/widgets/WebViewStack.hpp b/src/widgets/WebViewStack.hpp
index 27a1181..5a88266 100644
--- a/src/widgets/WebViewStack.hpp
+++ b/src/widgets/WebViewStack.hpp
@@ -62,6 +62,7 @@ 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 expose_rpc_function(const QString &name, const RpcFunc &action,
WebViewId webview_id) override;