aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAkshay Nair <phenax5@gmail.com>2025-05-01 22:21:48 +0530
committerAkshay Nair <phenax5@gmail.com>2025-05-02 16:20:48 +0530
commit86496d0ce96f2e57f3f301c08488b6421321b3c9 (patch)
tree2dd20f110c0eec27748b94e6fb84f19fb85ba6d0
parentfab5f4d2fd80eb288265c64ba390460ac974ee81 (diff)
downloadnull-browser-86496d0ce96f2e57f3f301c08488b6421321b3c9.tar.gz
null-browser-86496d0ce96f2e57f3f301c08488b6421321b3c9.zip
Add PermissionRequested event and accept/reject handling
-rw-r--r--TODO.org5
-rw-r--r--init.lua19
-rw-r--r--lua/null-browser/defaults/vi.lua9
-rw-r--r--lua/null-browser/stdlib.lua3
-rw-r--r--src/events.hpp110
-rw-r--r--src/widgets/WebViewStack.cpp24
-rw-r--r--src/widgets/WebViewStack.hpp1
7 files changed, 147 insertions, 24 deletions
diff --git a/TODO.org b/TODO.org
index 61d7cef..d869829 100644
--- a/TODO.org
+++ b/TODO.org
@@ -9,8 +9,9 @@
- [X] Scroll api (for j/k/h/l/gg/G)
- [X] table extend/merge
- [X] window.opener controls (use tab for now?)
-- [ ] Tests for api
-- [ ] Permission requests handling/persisting
+- [X] Permission requests handling
+- [-] Tests for api
+- [ ] Permission requests persist
- [ ] Notifications
- [ ] Fullscreen
- [ ] Zoom in/out
diff --git a/init.lua b/init.lua
index 74d85e0..af0458c 100644
--- a/init.lua
+++ b/init.lua
@@ -2,7 +2,7 @@ print('hello starting up...')
web.set('new_view_url', 'https://lite.duckduckgo.com')
web.set('close_window_when_no_views', true)
-web.set('user_agent', 'MacOS | Safari - $500 edition')
+-- web.set('user_agent', 'MacOS | Safari - $500 edition')
web.set('downloads_dir', os.getenv('HOME') .. '/Downloads/firefox')
local dmenu = require 'null-browser.extras.dmenu'
@@ -16,7 +16,22 @@ search_engines.urls['ld'] = 'https://lite.duckduckgo.com/?q={}'
require 'null-browser.defaults.vi'.setup {
menu = dmenu,
history = history,
- preprocess_url = search_engines.preprocess_url,
+ transform_url_input = search_engines.transform_url_input,
}
+web.event.add_listener('PermissionRequested', {
+ callback = function(event)
+ dmenu.select({ 'Allow', 'Deny' }, { prompt = 'Requesting permission for ' .. event.permission_type },
+ function(err, choice)
+ if err then return print('-- x permission ignored') end
+ print('----- permission', event.permission_type, web.inspect(choice))
+ if web.utils.string_trim(choice) == 'Allow' then
+ event.accept()
+ else
+ event.reject()
+ end
+ end)
+ end,
+})
+
print('ending...')
diff --git a/lua/null-browser/defaults/vi.lua b/lua/null-browser/defaults/vi.lua
index 1b72f60..cefbbd2 100644
--- a/lua/null-browser/defaults/vi.lua
+++ b/lua/null-browser/defaults/vi.lua
@@ -1,11 +1,11 @@
local M = {}
+
local config = {
menu = require 'null-browser.extras.dmenu',
history = require 'null-browser.extras.history',
- preprocess_url = web.utils.string_trim,
+ transform_url_input = web.utils.string_trim,
}
-
function M.setup(opts)
config = web.utils.table_merge(config, opts)
end
@@ -26,17 +26,16 @@ function M.initialize()
-- Open in new view
web.keymap.set('n', 'o', function()
- print(web.get('new_view_url'), web.get('user_agent'))
config.menu.select(config.history.list(), { prompt = 'Open view:' }, function(err, result)
if err or not result then return end
- web.view.create(config.preprocess_url(result))
+ web.view.create(config.transform_url_input(result))
end)
end)
-- Open in current view
web.keymap.set('n', '<s-o>', function()
config.menu.select(config.history.list(), { prompt = 'Open:' }, function(err, result)
if err or not result then return end
- web.view.set_url(config.preprocess_url(result))
+ web.view.set_url(config.transform_url_input(result))
end)
end)
-- Delete from history
diff --git a/lua/null-browser/stdlib.lua b/lua/null-browser/stdlib.lua
index f96dd48..737f747 100644
--- a/lua/null-browser/stdlib.lua
+++ b/lua/null-browser/stdlib.lua
@@ -7,6 +7,9 @@ local inspector_loaded, inspector = pcall(require, 'inspect')
if inspector_loaded then
web.inspect = inspector.inspect
else
+ --- Returns human-readable string representation of Lua tables
+ ---
+ --- @link https://github.com/kikito/inspect.lua
web.inspect = function(val)
print('[warn] "inspect" module not loaded'); return val
end
diff --git a/src/events.hpp b/src/events.hpp
index ffa4f8b..f16c2a1 100644
--- a/src/events.hpp
+++ b/src/events.hpp
@@ -1,8 +1,12 @@
#pragma once
#include <QtCore>
+#include <functional>
#include <lua.hpp>
+#include <memory>
+#include <qwebenginepermission.h>
+#include "LuaRuntime.hpp"
#include "lua.h"
#include "widgets/BrowserWindow.hpp"
#include "widgets/WebViewStack.hpp"
@@ -15,30 +19,126 @@
class BrowserEvent {
public:
QString kind = "-";
- virtual ~BrowserEvent() = default;
- virtual void lua_push(lua_State *state) const { lua_newtable(state); };
+ virtual void lua_push(lua_State *state) const {
+ lua_newtable(state);
+ SET_FIELD("type", string, kind.toStdString().c_str())
+ };
};
class UrlChangedEvent : public BrowserEvent {
public:
- const QString url;
+ const QString &url;
const WebViewId webview_id;
const WindowId win_id;
- UrlChangedEvent(QString url, WebViewId webview_id, WindowId win_id)
- : url(std::move(url)), webview_id(webview_id), win_id(win_id) {
+ UrlChangedEvent(const QString &url, WebViewId webview_id, WindowId win_id)
+ : url(url), webview_id(webview_id), win_id(win_id) {
kind = "UrlChanged";
}
void lua_push(lua_State *state) const override {
lua_newtable(state);
+ SET_FIELD("type", string, kind.toStdString().c_str())
SET_FIELD("view_id", integer, webview_id)
SET_FIELD("win_id", integer, win_id)
SET_FIELD("url", string, url.toStdString().c_str())
}
};
+class PermissionRequestEvent : public BrowserEvent {
+public:
+ const QWebEnginePermission::PermissionType premission_type;
+ const WebViewId webview_id;
+ const WindowId win_id;
+ std::function<void()> accept_request;
+ std::function<void()> reject_request;
+
+ PermissionRequestEvent(QWebEnginePermission::PermissionType type,
+ std::function<void()> accept_request, std::function<void()> reject_request,
+ WebViewId webview_id, WindowId win_id)
+ : premission_type(type), webview_id(webview_id), win_id(win_id),
+ accept_request(std::move(accept_request)), reject_request(std::move(reject_request)) {
+ kind = "PermissionRequested";
+ }
+
+ static PermissionRequestEvent *from_permission(std::shared_ptr<QWebEnginePermission> &permission,
+ WebViewId webview_id, WindowId win_id) {
+ auto accept = [permission]() {
+ QMetaObject::invokeMethod(qApp, [=]() { permission->grant(); }, Qt::QueuedConnection);
+ };
+ auto reject = [permission]() {
+ QMetaObject::invokeMethod(qApp, [=]() { permission->deny(); }, Qt::QueuedConnection);
+ };
+ // TODO: Manage delete for permission object
+ return new PermissionRequestEvent(permission->permissionType(), accept, reject, webview_id,
+ win_id);
+ }
+
+ [[nodiscard]] const char *permission_type() const {
+ switch (premission_type) {
+ case QWebEnginePermission::PermissionType::MediaAudioCapture:
+ return "MediaAudioCapture";
+ case QWebEnginePermission::PermissionType::MediaVideoCapture:
+ return "MediaVideoCapture";
+ case QWebEnginePermission::PermissionType::MediaAudioVideoCapture:
+ return "MediaAudioVideoCapture";
+ case QWebEnginePermission::PermissionType::DesktopVideoCapture:
+ return "DesktopVideoCapture";
+ case QWebEnginePermission::PermissionType::DesktopAudioVideoCapture:
+ return "DesktopAudioVideoCapture";
+ case QWebEnginePermission::PermissionType::MouseLock:
+ return "MouseLock";
+ case QWebEnginePermission::PermissionType::Notifications:
+ return "Notifications";
+ case QWebEnginePermission::PermissionType::Geolocation:
+ return "Geolocation";
+ case QWebEnginePermission::PermissionType::ClipboardReadWrite:
+ return "ClipboardReadWrite";
+ case QWebEnginePermission::PermissionType::LocalFontsAccess:
+ return "LocalFontsAccess";
+ default:
+ return "Unsupported";
+ }
+ }
+
+ void lua_push(lua_State *state) const override {
+ lua_newtable(state);
+ SET_FIELD("type", string, kind.toStdString().c_str())
+ SET_FIELD("permission_type", string, permission_type())
+ SET_FIELD("view_id", integer, webview_id)
+ SET_FIELD("win_id", integer, win_id)
+
+ lua_pushstring(state, "accept");
+ lua_pushlightuserdata(state, (void *)this);
+ lua_pushcclosure(
+ state,
+ [](lua_State *state) {
+ void *userdata = lua_touserdata(state, lua_upvalueindex(1));
+ auto *event = static_cast<PermissionRequestEvent *>(userdata);
+ event->accept_request();
+ lua_pushnil(state);
+ return 1;
+ },
+ 1);
+ lua_settable(state, -3);
+
+ lua_pushstring(state, "reject");
+ lua_pushlightuserdata(state, (void *)this);
+ lua_pushcclosure(
+ state,
+ [](lua_State *state) {
+ void *userdata = lua_touserdata(state, lua_upvalueindex(1));
+ auto *event = static_cast<PermissionRequestEvent *>(userdata);
+ event->reject_request();
+ lua_pushnil(state);
+ return 1;
+ },
+ 1);
+ lua_settable(state, -3);
+ }
+};
+
struct EventHandlerRequest {
std::vector<QString> event_names;
std::vector<QString> patterns;
diff --git a/src/widgets/WebViewStack.cpp b/src/widgets/WebViewStack.cpp
index c48a363..7fe9218 100644
--- a/src/widgets/WebViewStack.cpp
+++ b/src/widgets/WebViewStack.cpp
@@ -5,6 +5,7 @@
#include <QWebEngineProfile>
#include <cstdint>
#include <cstdlib>
+#include <memory>
#include <vector>
#include "WindowActionRouter.hpp"
@@ -48,17 +49,22 @@ WebView *WebViewStack::create_new_webview(const QUrl &url, bool focus) {
layout->addWidget(webview);
webview_list.append(webview);
- connect(webview->page(), &QWebEnginePage::newWindowRequested, this,
- &WebViewStack::on_new_webview_request);
- connect(webview->page(), &QWebEnginePage::urlChanged, this, [webview](const QUrl &url) {
+ auto *page = webview->page();
+ connect(page, &QWebEnginePage::newWindowRequested, this, &WebViewStack::on_new_webview_request);
+ connect(page, &QWebEnginePage::urlChanged, this, [webview](const QUrl &url) {
// TODO: Add window id
- WindowActionRouter::instance().dispatch_event(
- new UrlChangedEvent(url.toString(), webview->get_id(), 0));
+ auto *event = new UrlChangedEvent(url.toString(), webview->get_id(), 0);
+ WindowActionRouter::instance().dispatch_event(event);
+ });
+ connect(page, &QWebEnginePage::titleChanged, this, [this](const QString & /* title */) {
+ emit current_webview_title_changed(layout->currentIndex());
+ });
+ connect(page, &QWebEnginePage::permissionRequested, this, [webview](QWebEnginePermission perm) {
+ auto permission = std::make_shared<QWebEnginePermission>(std::move(perm));
+ // TODO: Add windown id
+ auto *event = PermissionRequestEvent::from_permission(permission, webview->get_id(), 0);
+ WindowActionRouter::instance().dispatch_event(event);
});
- connect(webview->page(), &QWebEnginePage::titleChanged, this,
- [this](const QString & /* title */) {
- emit current_webview_title_changed(layout->currentIndex());
- });
if (focus)
focus_webview(webview->get_id());
diff --git a/src/widgets/WebViewStack.hpp b/src/widgets/WebViewStack.hpp
index 1b8b288..cee0593 100644
--- a/src/widgets/WebViewStack.hpp
+++ b/src/widgets/WebViewStack.hpp
@@ -3,7 +3,6 @@
#include <QStackedLayout>
#include <QWebEngineProfile>
#include <cstdint>
-#include <functional>
#include <qwebengineprofile.h>
#include <vector>