Better profile support, only 1 web server per context, multiple windows per context

This commit is contained in:
2026-03-12 00:23:27 +01:00
parent 7d234bc834
commit d99c5a1725
13 changed files with 314 additions and 168 deletions

View File

@@ -26,7 +26,6 @@
#define COMMAND_FILE_OPEN 20
#define COMMAND_FILE_SAVE 21
#define COMMAND_SET_OU_TOKEN 22
#define COMMAND_SET_CERTIFICATE 23
class Command
{

View File

@@ -22,7 +22,9 @@ int main(int argc, char *argv[])
rkt_webview_init();
wv1 = rkt_webview_create(0, eventCb, nullptr);
int context = rkt_webview_new_context("console.log('boilerplate!');", nullptr);
wv1 = rkt_webview_create(context, 0, eventCb);
rkt_webview_move(wv1, 200, 300);
rkt_webview_resize(wv1, 800, 600);
@@ -57,7 +59,7 @@ int main(int argc, char *argv[])
if (i == 10) {
wv2 = rkt_webview_create(wv1, eventCb, nullptr);
wv2 = rkt_webview_create(context, wv1, eventCb);
rkt_webview_move(wv2, 400, 200);
rkt_webview_resize(wv2, 800, 600);
rkt_webview_set_url(wv2, "https://127.0.0.1");

View File

@@ -32,10 +32,17 @@ void rkt_webview_init()
}
}
int rkt_webview_create(rktwebview_t parent, event_cb_t js_event_cb, const char *optional_server_cert_pem)
rkt_wv_context_t rkt_webview_new_context(const char *boilerplate_js, const char *optional_server_cert_pem)
{
rkt_webview_init();
return handler->rktWebViewCreate(parent, js_event_cb, optional_server_cert_pem);
return handler->newContext(boilerplate_js, optional_server_cert_pem);
}
int rkt_webview_create(rkt_wv_context_t context, rktwebview_t parent, event_cb_t js_event_cb)
{
rkt_webview_init();
return handler->rktWebViewCreate(context, parent, js_event_cb);
}
void rkt_webview_close(rktwebview_t wv)

View File

@@ -10,6 +10,7 @@
extern "C" {
typedef int rktwebview_t;
typedef int rkt_wv_context_t;
typedef struct {
rktwebview_t wv;
@@ -81,12 +82,12 @@ typedef void (*event_cb_t)(rkt_data_t *);
RKTWEBVIEW_QT_EXPORT void rkt_webview_init();
RKTWEBVIEW_QT_EXPORT void rkt_webview_process_events(int for_ms);
RKTWEBVIEW_QT_EXPORT void rkt_webview_free_data(rkt_data_t *d);
RKTWEBVIEW_QT_EXPORT rkt_data_t *rkt_webview_version();
RKTWEBVIEW_QT_EXPORT int rkt_webview_create(rktwebview_t parent, event_cb_t js_event_cb, const char *optional_server_cert_pem);
RKTWEBVIEW_QT_EXPORT rkt_wv_context_t rkt_webview_new_context(const char *boilerplate_js, const char *optional_server_cert_pem);
RKTWEBVIEW_QT_EXPORT int rkt_webview_create(rkt_wv_context_t context, rktwebview_t parent, event_cb_t js_event_cb);
RKTWEBVIEW_QT_EXPORT void rkt_webview_close(rktwebview_t wv);
RKTWEBVIEW_QT_EXPORT bool rkt_webview_valid(rktwebview_t wv);
RKTWEBVIEW_QT_EXPORT result_t rkt_webview_set_title(rktwebview_t wv, const char *title);

View File

@@ -9,6 +9,10 @@
#include <QJsonDocument>
#include <QJsonObject>
#include <QAbstractEventDispatcher>
#include <QWebEngineProfile>
#include <QWebEngineProfileBuilder>
#include <QWebEngineScriptCollection>
#include <QWebEngineScript>
#include "command.h"
#include <QFileDialog>
@@ -29,14 +33,12 @@ void Rktwebview_qt::processCommand(Command *cmd)
}
break;
case COMMAND_CREATE: {
int parent = cmd->args[0].toInt();
rkt_wv_context_t context = cmd->args[0].toInt();
rktwebview_t parent = cmd->args[1].toInt();
void *f = cmd->args[1].value<void *>();
void *f = cmd->args[2].value<void *>();
event_cb_t js_event_cb = reinterpret_cast <event_cb_t>(f);
bool has_scp = cmd->args[2].toBool();
QByteArray scp_pem = cmd->args[3].toByteArray();
WebviewWindow *p;
if (_views.contains(parent)) {
p = _views[parent];
@@ -44,22 +46,28 @@ void Rktwebview_qt::processCommand(Command *cmd)
p = nullptr;
}
WebviewWindow *w = new WebviewWindow(p, has_scp, scp_pem);
WebViewQt *view = new WebViewQt(nextHandle(), w);
w->addView(view, this);
if (!_contexts.contains(context)) {
cmd->result = -1;
cmd->done = true;
} else {
QWebEngineProfile *profile = _contexts[context];
WebviewWindow *w = new WebviewWindow(profile, p);
WebViewQt *view = new WebViewQt(nextHandle(), w);
w->addView(view, this);
int id = view->id();
int id = view->id();
_views[id] = w;
_view_js_callbacks[id] = js_event_cb;
_views[id] = w;
_view_js_callbacks[id] = js_event_cb;
w->show();
while(!w->windowCreated()) {
doEvents();
w->show();
while(!w->windowCreated()) {
doEvents();
}
cmd->result = id;
cmd->done = true;
}
cmd->result = id;
cmd->done = true;
}
break;
case COMMAND_CLOSE: {
@@ -370,17 +378,68 @@ int Rktwebview_qt::nextHandle()
return h;
}
int Rktwebview_qt::rktWebViewCreate(int parent, event_cb_t js_evt_cb, const char *optional_server_cert_pem)
rkt_wv_context_t Rktwebview_qt::newContext(const char *boilerplate_js, const char *optional_server_cert_pem)
{
QWebEngineProfileBuilder b;
if (optional_server_cert_pem != nullptr) {
QByteArray scp = QByteArray(optional_server_cert_pem);
QList<QSslCertificate> certs;
QSslCertificate cert(scp);
certs.append(cert);
b.setAdditionalTrustedCertificates(certs);
}
_context_counter += 1;
QString name = QString::asprintf("profile-%d", _context_counter);
QString code = "if (window.rkt_event_queue === undefined) { window.rkt_event_queue = []; }\n"
"window.rkt_send_event = function(obj) {\n"
" console.log('Sending event: ' + obj);\n"
" window.rkt_event_queue.push(obj);\n"
"};\n"
"window.rkt_get_events = function() {\n"
" let q = window.rkt_event_queue;\n"
" window.rkt_event_queue = [];\n"
" let json_q = JSON.stringify(q);\n"
" return json_q;\n"
"};\n"
"";
QList<QWebEngineScript> scripts;
QWebEngineProfile *p = b.createProfile(name);
QWebEngineScriptCollection *col = p->scripts();
QWebEngineScript s1;
s1.setInjectionPoint(QWebEngineScript::InjectionPoint::DocumentReady);
s1.setName("eventing");
s1.setSourceCode(code);
s1.setWorldId(QWebEngineScript::MainWorld);
scripts.append(s1);
QWebEngineScript s2;
s2.setInjectionPoint(QWebEngineScript::InjectionPoint::DocumentReady);
s2.setName("boilerplate");
s2.setSourceCode(boilerplate_js);
s2.setWorldId(QWebEngineScript::MainWorld);
scripts.append(s2);
col->insert(scripts);
QWebEngineScriptCollection *c = p->scripts();
QList<QWebEngineScript> l = c->toList();
printf("%d\n", l.size());
_contexts[_context_counter] = p;
return _context_counter;
}
int Rktwebview_qt::rktWebViewCreate(rkt_wv_context_t context, rktwebview_t parent, event_cb_t js_evt_cb)
{
Command c(COMMAND_CREATE);
c.args.push_back(context);
c.args.push_back(parent);
void *function = reinterpret_cast<void *>(js_evt_cb);
QVariant f(QVariant::fromValue(function));
c.args.push_back(f);
bool has_scp = (optional_server_cert_pem != nullptr);
c.args.push_back(has_scp);
QByteArray scp = (optional_server_cert_pem == nullptr) ? QByteArray("") : QByteArray(optional_server_cert_pem);
c.args.push_back(scp);
postCommand(&c);
@@ -659,7 +718,7 @@ void Rktwebview_qt::onPageLoad(rktwebview_t w)
void Rktwebview_qt::pageLoaded(rktwebview_t w, bool ok)
{
runJs(w,
/*runJs(w,
"if (window.rkt_event_queue === undefined) { window.rkt_event_queue = []; }\n"
"window.rkt_send_event = function(obj) {\n"
" console.log('Sending event: ' + obj);\n"
@@ -672,6 +731,20 @@ void Rktwebview_qt::pageLoaded(rktwebview_t w, bool ok)
" return json_q;\n"
"};\n"
);
*/
if (!ok) {
// Inject code of the profile to this page
WebviewWindow *win = _views[w];
QWebEngineProfile *p = win->profile();
QWebEngineScriptCollection *col = p->scripts();
QList<QWebEngineScript> l = col->toList();
int i;
for(i = 0; i < l.size(); i++) {
runJs(w, l[i].sourceCode().toUtf8().constData());
}
}
// trigger page loaded.
EventContainer e("page-loaded");
@@ -775,6 +848,8 @@ Rktwebview_qt::Rktwebview_qt(Rktwebview_qt **handler) :
_argc = 1;
_argv[0] = const_cast<char *>("Rktwebview_qt");
_context_counter = 0;
_current_handle = 0;
_handler = handler;
@@ -800,3 +875,19 @@ Rktwebview_qt::Rktwebview_qt(Rktwebview_qt **handler) :
*_handler = nullptr;
}
Rktwebview_qt::~Rktwebview_qt()
{
QList<int> win_keys = _views.keys();
int i, N;
for(i = 0, N = win_keys.size(); i < N; i++) {
WebviewWindow *w = _views[win_keys[i]];
delete w;
}
QList<int> c_keys = _contexts.keys();
for(i = 0, N = c_keys.size(); i < N; i++) {
QWebEngineProfile *p = _contexts[c_keys[i]];
delete p;
}
}

View File

@@ -26,8 +26,11 @@ class RKTWEBVIEW_QT_EXPORT Rktwebview_qt : public QObject
private:
QApplication *_app;
rktwebview_t _current_handle;
int _context_counter;
QHash<int, QWebEngineProfile *> _contexts;
QHash<int, WebviewWindow *> _views;
QHash<int, event_cb_t> _view_js_callbacks;
QHash<int, event_cb_t> _view_js_callbacks;
QTimer _process_events;
@@ -64,7 +67,8 @@ public:
public:
int nextHandle();
public:
int rktWebViewCreate(int parent, event_cb_t js_evt_cb, const char *optional_server_cert_pem);
rkt_wv_context_t newContext(const char *boilerplate_js, const char *optional_server_cert_pem);
int rktWebViewCreate(rkt_wv_context_t context, rktwebview_t parent, event_cb_t js_evt_cb);
void rktWebViewClose(int wv);
void rktSetOUToken(rktwebview_t wv, const char *ou_token);
void rktQuit();
@@ -110,6 +114,7 @@ public:
public:
Rktwebview_qt(Rktwebview_qt **handler);
~Rktwebview_qt();
};

View File

@@ -10,6 +10,7 @@ WebViewQt::WebViewQt(int id, WebviewWindow *window)
_window = window;
}
int WebViewQt::id() const
{
return _id;

View File

@@ -23,7 +23,6 @@ signals:
public:
WebViewQt(int id, WebviewWindow *window);
};
#endif // WEBVIEWQT_H

View File

@@ -36,7 +36,7 @@ static void displ1(QSslCertificate &cert, QList<QSslCertificate::SubjectInfo > l
};
*/
WebviewWindow::WebviewWindow(WebviewWindow *parent, bool has_scp, QByteArray scp_pem)
WebviewWindow::WebviewWindow(QWebEngineProfile *profile, WebviewWindow *parent)
: QMainWindow{parent}
{
static int profile_nr = 0;
@@ -51,21 +51,7 @@ WebviewWindow::WebviewWindow(WebviewWindow *parent, bool has_scp, QByteArray scp
_moved = 0;
_resized = 0;
QWebEngineProfileBuilder b;
if (has_scp) {
profile_nr += 1;
char buf[100];
sprintf(buf, "profile-%d", profile_nr);
QString name(buf);
QSslCertificate cert(scp_pem);
QList<QSslCertificate> certs;
certs.append(cert);
b.setAdditionalTrustedCertificates(certs);
_profile = b.createProfile(name);
} else {
_profile = QWebEngineProfile::defaultProfile();
}
_profile = profile;
if (parent != nullptr) {
setWindowModality(Qt::WindowModality::WindowModal);
@@ -78,7 +64,9 @@ WebviewWindow::WebviewWindow(WebviewWindow *parent, bool has_scp, QByteArray scp
void WebviewWindow::navigationRequested(QWebEngineNavigationRequest &req)
{
if (req.navigationType() == QWebEngineNavigationRequest::NavigationType::TypedNavigation) {
QWebEngineNavigationRequest::NavigationType t = req.navigationType();
if (t == QWebEngineNavigationRequest::NavigationType::TypedNavigation ||
t == QWebEngineNavigationRequest::RedirectNavigation) {
req.accept();
} else {
EventContainer e("navigation-request");
@@ -226,8 +214,9 @@ void WebviewWindow::addView(WebViewQt *v, Rktwebview_qt *c)
_view = v;
this->setCentralWidget(v);
QWebEnginePage *page;
QWebEnginePage *page = _view->page();
/*
if (_profile == nullptr) {
page = _view->page();
} else {
@@ -255,6 +244,7 @@ void WebviewWindow::addView(WebViewQt *v, Rktwebview_qt *c)
);
evt_script.setWorldId(QWebEngineScript::ApplicationWorld);
//col.insert(evt_script);
*/
connect(page, &QWebEnginePage::loadFinished, this, [this](bool ok) {
_container->pageLoaded(_view->id(), ok);

View File

@@ -65,7 +65,7 @@ public:
void openDevTools();
public:
explicit WebviewWindow(WebviewWindow *parent, bool has_scp, QByteArray scp_pem);
explicit WebviewWindow(QWebEngineProfile *profile, WebviewWindow *parent);
private slots:
void triggerResize();