aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAkshay Nair <phenax5@gmail.com>2025-07-26 16:16:54 +0530
committerAkshay Nair <phenax5@gmail.com>2025-07-26 16:16:54 +0530
commit8ab1f8d796d8f35a65e38fdd03d51824a41d1dab (patch)
tree6b7b9655e5a2e7f6fbc0e1b3bec66771e87a64f8
parent2078c60477842d7cf6991148e23979565737a8b5 (diff)
downloadnull-browser-8ab1f8d796d8f35a65e38fdd03d51824a41d1dab.tar.gz
null-browser-8ab1f8d796d8f35a65e38fdd03d51824a41d1dab.zip
Add simple js rpc for decorations via custom scheme
Diffstat (limited to '')
-rw-r--r--init.lua12
-rw-r--r--lua/null-browser/extras/tabline.lua3
-rw-r--r--spec/main.cpp2
-rw-r--r--src/main.cpp2
-rw-r--r--src/schemes/NullRpcSchemeHandler.hpp47
-rw-r--r--src/schemes/schemes.hpp16
-rw-r--r--src/widgets/BrowserApp.cpp4
-rw-r--r--src/widgets/BrowserApp.hpp2
-rw-r--r--src/widgets/DevtoolsWindow.hpp31
-rw-r--r--src/widgets/EdgeDecoration.cpp24
-rw-r--r--src/widgets/WebView.cpp38
-rw-r--r--src/widgets/WebView.hpp37
12 files changed, 179 insertions, 39 deletions
diff --git a/init.lua b/init.lua
index 6b9abed..30e939c 100644
--- a/init.lua
+++ b/init.lua
@@ -68,12 +68,12 @@ end
require 'null-browser.extras.tabline'.init()
-- Decorations config
-web.event.add_listener('WinCreated', {
- callback = function(event)
- web.decorations.bottom.enable({ win = event.win_id })
- start_clock(event.win_id)
- end,
-})
+-- web.event.add_listener('WinCreated', {
+-- callback = function(event)
+-- web.decorations.bottom.enable({ win = event.win_id })
+-- start_clock(event.win_id)
+-- end,
+-- })
web.keymap.set('n', '<space>gg', function()
if web.decorations.bottom.is_enabled({ win = 0 }) then
diff --git a/lua/null-browser/extras/tabline.lua b/lua/null-browser/extras/tabline.lua
index c278df5..e8edbae 100644
--- a/lua/null-browser/extras/tabline.lua
+++ b/lua/null-browser/extras/tabline.lua
@@ -41,7 +41,8 @@ function tabline.tabs_html()
local text = index .. ': ' .. view.title .. ' (' .. view.url .. ')'
local classes = 'tab'
if web.view.current() == view.id then classes = classes .. ' current' end
- local html = '<div class="' .. classes .. '">' .. text .. '</div>'
+ local onclick = '__nullbrowser.tab_select({ view: ' .. view.id .. ' })'
+ local html = '<div class="' .. classes .. '" onclick="' .. onclick .. '">' .. text .. '</div>'
views_html = views_html .. html
end
diff --git a/spec/main.cpp b/spec/main.cpp
index 6de21e2..83ab694 100644
--- a/spec/main.cpp
+++ b/spec/main.cpp
@@ -1,8 +1,10 @@
#include <QtCore>
+#include "schemes/schemes.hpp"
#include "testUtils.h"
int main(int argc, char **argv) {
+ register_all_schemes();
QApplication app(argc, argv);
int exit_code_app = run_app_tests();
diff --git a/src/main.cpp b/src/main.cpp
index fbe1fbd..76afb90 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,8 +1,10 @@
#include <QApplication>
#include "App.hpp"
+#include "schemes/schemes.hpp"
int main(int argc, char *argv[]) {
+ register_all_schemes();
const QApplication qt_app(argc, argv);
App app;
diff --git a/src/schemes/NullRpcSchemeHandler.hpp b/src/schemes/NullRpcSchemeHandler.hpp
new file mode 100644
index 0000000..b1d5168
--- /dev/null
+++ b/src/schemes/NullRpcSchemeHandler.hpp
@@ -0,0 +1,47 @@
+#pragma once
+
+#include <QWebEngineUrlRequestJob>
+#include <QWebEngineUrlSchemeHandler>
+#include <QtCore>
+#include <qurlquery.h>
+
+class NullRPCSchemeHandler : public QWebEngineUrlSchemeHandler {
+ Q_OBJECT
+
+public:
+ static NullRPCSchemeHandler &instance() {
+ static NullRPCSchemeHandler handler;
+ return handler;
+ }
+
+ void requestStarted(QWebEngineUrlRequestJob *job) override {
+ auto url = job->requestUrl();
+ qDebug() << "REQ" << url << url.host();
+
+ if (url.host().isEmpty() || url.host() == "noop") {
+ job->reply("text/html", new QBuffer(job));
+ return;
+ }
+
+ if (url.host() == "log") {
+ qDebug() << url.query();
+ job->reply("text/plain", new QBuffer(job));
+ return;
+ }
+
+ QUrlQuery query(url.query());
+ emit message_received(url.host(), query);
+
+ QByteArray data = "{}";
+ QBuffer *buffer = new QBuffer(job);
+ buffer->setData(data);
+ buffer->open(QIODevice::ReadOnly);
+ job->reply("text/plain", buffer);
+ }
+
+signals:
+ void message_received(const QString &action, QUrlQuery params);
+
+private:
+ NullRPCSchemeHandler() = default;
+};
diff --git a/src/schemes/schemes.hpp b/src/schemes/schemes.hpp
new file mode 100644
index 0000000..f04dc4d
--- /dev/null
+++ b/src/schemes/schemes.hpp
@@ -0,0 +1,16 @@
+#pragma once
+
+#include <QWebEngineUrlScheme>
+#include <qwebengineurlscheme.h>
+
+void register_nullrpc_scheme() {
+ QWebEngineUrlScheme scheme("nullrpc");
+ scheme.setFlags(
+ QWebEngineUrlScheme::SecureScheme | QWebEngineUrlScheme::LocalScheme |
+ QWebEngineUrlScheme::LocalAccessAllowed | QWebEngineUrlScheme::ServiceWorkersAllowed |
+ QWebEngineUrlScheme::ContentSecurityPolicyIgnored | QWebEngineUrlScheme::FetchApiAllowed);
+ scheme.setSyntax(QWebEngineUrlScheme::Syntax::Path);
+ QWebEngineUrlScheme::registerScheme(scheme);
+}
+
+void register_all_schemes() { register_nullrpc_scheme(); }
diff --git a/src/widgets/BrowserApp.cpp b/src/widgets/BrowserApp.cpp
index d096f01..922c5cc 100644
--- a/src/widgets/BrowserApp.cpp
+++ b/src/widgets/BrowserApp.cpp
@@ -7,6 +7,7 @@
#include "WindowActionRouter.hpp"
#include "events/NotificationReceivedEvent.hpp"
#include "events/WinCreatedEvent.hpp"
+#include "schemes/NullRpcSchemeHandler.hpp"
#include "widgets/BrowserWindow.hpp"
#include "widgets/BrowserApp.hpp"
@@ -36,7 +37,7 @@ BrowserApp::BrowserApp(Configuration &configuration) : configuration(configurati
}
// Initializes profile
- for (auto *profile : profiles) {
+ for (auto *profile : profiles()) {
setup_profile(profile);
}
@@ -52,6 +53,7 @@ void BrowserApp::setup_profile(QWebEngineProfile *profile) {
WindowActionRouter::instance().dispatch_event(event);
});
profile->setPersistentPermissionsPolicy(configuration.permission_persistance_policy());
+ profile->installUrlSchemeHandler("nullrpc", &NullRPCSchemeHandler::instance());
}
BrowserWindow *BrowserApp::create_window(const QStringList &urls) {
diff --git a/src/widgets/BrowserApp.hpp b/src/widgets/BrowserApp.hpp
index dcfe6f4..d29dc1b 100644
--- a/src/widgets/BrowserApp.hpp
+++ b/src/widgets/BrowserApp.hpp
@@ -19,7 +19,7 @@ protected:
private:
Configuration &configuration;
QWebEngineProfile default_profile{"default"};
- QList<QWebEngineProfile *> profiles{&default_profile};
+ QList<QWebEngineProfile *> profiles() { return {&default_profile}; }
void setup_profile(QWebEngineProfile *profile);
};
diff --git a/src/widgets/DevtoolsWindow.hpp b/src/widgets/DevtoolsWindow.hpp
new file mode 100644
index 0000000..b277c89
--- /dev/null
+++ b/src/widgets/DevtoolsWindow.hpp
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <QMainWindow>
+#include <QWebEngineProfile>
+#include <QWebEngineView>
+#include <QtCore>
+
+#include "utils.hpp"
+
+class DevtoolsWindow : public QMainWindow {
+ Q_OBJECT
+
+public:
+ DevtoolsWindow(QWebEngineProfile *profile, QWidget *parent = nullptr,
+ Qt::WindowFlags flags = Qt::WindowFlags())
+ : QMainWindow(parent, flags) {
+ webengineview = new QWebEngineView(profile, this);
+ this->setCentralWidget(webengineview);
+ }
+
+ DELEGATE(webengineview, page, page)
+
+signals:
+ void closed();
+
+protected:
+ void closeEvent(QCloseEvent * /* event */) override { emit closed(); }
+
+private:
+ QWebEngineView *webengineview;
+};
diff --git a/src/widgets/EdgeDecoration.cpp b/src/widgets/EdgeDecoration.cpp
index 7f5a59f..5277e6c 100644
--- a/src/widgets/EdgeDecoration.cpp
+++ b/src/widgets/EdgeDecoration.cpp
@@ -4,13 +4,26 @@
#include <qlabel.h>
#include <qwebengineview.h>
+#include "LuaRuntime.hpp"
#include "widgets/WebView.hpp"
#include "widgets/WebViewStack.hpp"
#include "widgets/EdgeDecoration.hpp"
QString default_html_layout = R"HTML(
- {{body}}
+ <script>
+ window.__nullbrowser ||= (() => {
+ const invoke = (action, opts = {}) => {
+ const urlParams = new URLSearchParams(opts);
+ const url = `nullrpc://${action}?${urlParams.toString()}`;
+ return fetch(url).then(r => r.json().catch(_ => null));
+ };
+ const api = new Proxy({}, {
+ get: (_, action) => (options = {}) => invoke(action, options),
+ });
+ return api;
+ })();
+ </script>
<style>
:where(html, body) {
margin: 0;
@@ -20,6 +33,8 @@ QString default_html_layout = R"HTML(
overflow: hidden;
}
</style>
+
+ {{body}}
)HTML";
EdgeDecoration::EdgeDecoration(bool vertical, QWebEngineProfile *profile, QWidget *parent)
@@ -50,9 +65,14 @@ void EdgeDecoration::setup_webview() {
if (!webview.has_value()) {
webview = new WebView(WebViewStack::next_webview_id++, profile, this);
layout()->addWidget(webview.value());
+ webview.value()->enable_rpc_api();
+ webview.value()->expose_rpc_function("tab_select", [](RpcArgs args) {
+ LuaRuntime::instance().webview_selected(args.at("view").toInt());
+ });
}
- webview.value()->setHtml(QString(default_html_layout).replace("{{body}}", html_content));
+ webview.value()->setHtml(QString(default_html_layout).replace("{{body}}", html_content),
+ QUrl("nullrpc://noop"));
if (vertical)
webview.value()->setFixedWidth(size);
else
diff --git a/src/widgets/WebView.cpp b/src/widgets/WebView.cpp
index 0ec2a74..604a502 100644
--- a/src/widgets/WebView.cpp
+++ b/src/widgets/WebView.cpp
@@ -1,7 +1,12 @@
#include <QMainWindow>
+#include <QWebEngineUrlRequestInterceptor>
#include <QWebEngineView>
#include <QtCore>
+#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)
@@ -23,10 +28,10 @@ void WebView::open_devtools() {
}
void WebView::scroll_increment(int deltax, int deltay) {
- auto code = QString(R"((() => {
+ auto code = QString(R"JS((() => {
const $el = document.scrollingElement;
$el.scrollTo($el.scrollLeft + %1, $el.scrollTop + %2);
- })())")
+ })())JS")
.arg(deltax)
.arg(deltay);
@@ -34,12 +39,35 @@ void WebView::scroll_increment(int deltax, int deltay) {
}
void WebView::scroll_to_top() {
- auto code = QString(R"(document.scrollingElement.scrollTo(0, 0))");
+ auto code = QString(R"JS(document.scrollingElement.scrollTo(0, 0))JS");
page()->runJavaScript(code);
}
void WebView::scroll_to_bottom() {
- auto code =
- QString(R"(document.scrollingElement.scrollTo(0, document.scrollingElement.scrollHeight))");
+ auto code = QString(
+ R"JS(document.scrollingElement.scrollTo(0, document.scrollingElement.scrollHeight))JS");
page()->runJavaScript(code);
}
+
+void WebView::enable_rpc_api() {
+ auto &nullrpc = NullRPCSchemeHandler::instance();
+ connect(&nullrpc, &NullRPCSchemeHandler::message_received, this, &WebView::on_rpc_message);
+}
+
+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;
+ return;
+ }
+
+ RpcArgs args;
+ for (auto pair : params.queryItems())
+ args.insert(pair);
+
+ auto func = exposed_functions.at(action);
+ func(args);
+}
diff --git a/src/widgets/WebView.hpp b/src/widgets/WebView.hpp
index 0900ea6..5be9831 100644
--- a/src/widgets/WebView.hpp
+++ b/src/widgets/WebView.hpp
@@ -1,35 +1,22 @@
#pragma once
#include <QMainWindow>
+#include <QWebEngineUrlRequestInterceptor>
+#include <QWebEngineUrlRequestJob>
+#include <QWebEngineUrlSchemeHandler>
#include <QWebEngineView>
#include <QtCore>
#include <cstdint>
+#include <functional>
#include <qtypes.h>
+#include <qurlquery.h>
+#include <unordered_map>
#include "utils.hpp"
+#include "widgets/DevtoolsWindow.hpp"
-class DevtoolsWindow : public QMainWindow {
- Q_OBJECT
-
-public:
- DevtoolsWindow(QWebEngineProfile *profile, QWidget *parent = nullptr,
- Qt::WindowFlags flags = Qt::WindowFlags())
- : QMainWindow(parent, flags) {
- webengineview = new QWebEngineView(profile, this);
- this->setCentralWidget(webengineview);
- }
-
- DELEGATE(webengineview, page, page)
-
-signals:
- void closed();
-
-protected:
- void closeEvent(QCloseEvent * /* event */) override { emit closed(); }
-
-private:
- QWebEngineView *webengineview;
-};
+using RpcArgs = std::unordered_map<QString, QVariant>;
+using RpcFunc = std::function<void(RpcArgs)>;
class WebView : public QWebEngineView {
Q_OBJECT
@@ -40,11 +27,15 @@ public:
void scroll_increment(int deltax, int deltay);
void scroll_to_top();
void scroll_to_bottom();
+ void enable_rpc_api();
+ void expose_rpc_function(const QString &name, const RpcFunc &action);
DEFINE_GETTER(get_id, id)
private:
uint32_t id;
-
DevtoolsWindow *devtools_window = nullptr;
+ std::unordered_map<QString, RpcFunc> exposed_functions;
+
+ void on_rpc_message(const QString &action, const QUrlQuery &params);
};