1069 lines
30 KiB
C++
1069 lines
30 KiB
C++
#include "rktwebview_qt.h"
|
|
#include "webviewqt.h"
|
|
#include "rktwebview_types.h"
|
|
#include "webviewwindow.h"
|
|
#include "rktutils.h"
|
|
#include "utils.h"
|
|
#include <QApplication>
|
|
#include <QTimer>
|
|
#include <QSemaphore>
|
|
#include <QJsonDocument>
|
|
#include <QJsonObject>
|
|
#include <QAbstractEventDispatcher>
|
|
#include <QWebEngineProfile>
|
|
#include <QWebEngineProfileBuilder>
|
|
#include <QWebEngineScriptCollection>
|
|
#include <QWebEngineScript>
|
|
#include "command.h"
|
|
#include <QFileDialog>
|
|
#include <QMessageBox>
|
|
#include <QAbstractButton>
|
|
|
|
static inline char *copyString(const char *s)
|
|
{
|
|
int l = strlen(s);
|
|
char *cpy = static_cast<char *>(malloc(l + 1));
|
|
memcpy(cpy, s, l + 1);
|
|
return cpy;
|
|
}
|
|
|
|
void Rktwebview_qt::processCommand(Command *cmd)
|
|
{
|
|
switch(cmd->cmd) {
|
|
case COMMAND_QUIT: { // Quit application
|
|
_app->quit();
|
|
cmd->done = true;
|
|
}
|
|
break;
|
|
case COMMAND_NEW_CONTEXT: {
|
|
QString boilerplate_js = cmd->args[0].toString();
|
|
bool has_pem = cmd->args[1].toBool();
|
|
QString optional_server_cert_pem = cmd->args[2].toString();
|
|
|
|
DEBUG1("bjs: %s\n", boilerplate_js.toUtf8().constData());
|
|
DEBUG1("oscp: %s\n", optional_server_cert_pem.toUtf8().constData());
|
|
|
|
QWebEngineProfileBuilder b;
|
|
if (has_pem) {
|
|
QByteArray scp = optional_server_cert_pem.toUtf8();
|
|
INFO1("Installing cert: %s\n", scp.constData());
|
|
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_evt_frame_el = null;\n"
|
|
"window.rkt_evt_frame_win = null;\n"
|
|
"window.rkt_send_event = function(obj) {\n"
|
|
" //console.log('Sending event: ' + obj);\n"
|
|
" window.rkt_event_queue.push(obj);\n"
|
|
" if (window.rkt_evt_frame_el) {\n"
|
|
" window.rkt_evt_frame_win.print();\n"
|
|
" }\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"
|
|
"// add hidden hover element to body if necessary\n"
|
|
"setInterval(function () {\n"
|
|
" if (window.rkt_evt_frame_el === null || window.rkt_evt_frame_el === undefined) {\n"
|
|
" window.rkt_evt_frame_el = document.createElement('iframe');\n"
|
|
" window.rkt_evt_frame_el.style.display = 'none';\n"
|
|
" window.rkt_evt_frame_el.setAttribute('id', 'rkt-evt-frame');\n"
|
|
" window.rkt_evt_frame_el.setAttribute('name', 'rkt-evt-frame');\n"
|
|
" document.body.append(window.rkt_evt_frame_el);\n"
|
|
" window.rkt_evt_frame_win = window.rkt_evt_frame_el.contentWindow;\n"
|
|
" } else {"
|
|
" if (window.rkt_event_queue.length > 0) {\n"
|
|
" window.rkt_evt_frame_win.print();\n"
|
|
" }\n"
|
|
" }\n"
|
|
"},\n"
|
|
"10);\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();
|
|
|
|
_contexts[_context_counter] = p;
|
|
|
|
cmd->result = _context_counter;
|
|
cmd->done = true;
|
|
}
|
|
break;
|
|
case COMMAND_CREATE: {
|
|
rkt_wv_context_t context = cmd->args[0].toInt();
|
|
rktwebview_t parent = cmd->args[1].toInt();
|
|
|
|
void *f = cmd->args[2].value<void *>();
|
|
event_cb_t js_event_cb = reinterpret_cast <event_cb_t>(f);
|
|
|
|
WebviewWindow *p;
|
|
if (_views.contains(parent)) {
|
|
p = _views[parent];
|
|
} else {
|
|
p = nullptr;
|
|
}
|
|
|
|
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();
|
|
|
|
_views[id] = w;
|
|
_view_js_callbacks[id] = js_event_cb;
|
|
|
|
w->show();
|
|
while(!w->windowCreated()) {
|
|
doEvents();
|
|
}
|
|
|
|
cmd->result = id;
|
|
cmd->done = true;
|
|
}
|
|
}
|
|
break;
|
|
case COMMAND_CLOSE: {
|
|
doEvents();
|
|
int wv = cmd->args[0].toInt();
|
|
if (_views.contains(wv)) {
|
|
WebviewWindow *w= _views[wv];
|
|
_views.remove(wv);
|
|
w->closeView();
|
|
cmd->result = true;
|
|
while(w->isVisible()) {
|
|
doEvents();
|
|
}
|
|
_view_js_callbacks.remove(wv);
|
|
delete w;
|
|
} else {
|
|
cmd->result = false;
|
|
}
|
|
cmd->done = true;
|
|
}
|
|
break;
|
|
case COMMAND_SET_OU_TOKEN: {
|
|
doEvents();
|
|
int wv = cmd->args[0].toInt();
|
|
QString ou_token = cmd->args[1].toString();
|
|
if (_views.contains(wv)) {
|
|
WebviewWindow *w= _views[wv];
|
|
w->setOUToken(ou_token);
|
|
cmd->result = true;
|
|
} else {
|
|
cmd->result = false;
|
|
}
|
|
cmd->done = true;
|
|
}
|
|
break;
|
|
case COMMAND_SET_URL: {
|
|
doEvents();
|
|
int wv = cmd->args[0].toInt();
|
|
QString url = cmd->args[1].toString();
|
|
if (_views.contains(wv)) {
|
|
WebviewWindow *w = _views[wv];
|
|
WebViewQt *v = w->view();
|
|
QUrl u(url);
|
|
v->setUrl(u);
|
|
cmd->result = true;
|
|
} else {
|
|
cmd->result = false;
|
|
}
|
|
cmd->done = true;
|
|
}
|
|
break;
|
|
case COMMAND_SET_HTML: {
|
|
doEvents();
|
|
int wv = cmd->args[0].toInt();
|
|
QString html = cmd->args[1].toString();
|
|
if (_views.contains(wv)) {
|
|
WebviewWindow *w = _views[wv];
|
|
WebViewQt *v = w->view();
|
|
v->setHtml(html);
|
|
cmd->result = true;
|
|
} else {
|
|
cmd->result = false;
|
|
}
|
|
cmd->done = true;
|
|
}
|
|
break;
|
|
case COMMAND_SET_TITLE: {
|
|
doEvents();
|
|
int wv = cmd->args[0].toInt();
|
|
QString title = cmd->args[1].toString();
|
|
if (_views.contains(wv)) {
|
|
WebviewWindow *w = _views[wv];
|
|
w->setWindowTitle(title);
|
|
cmd->result = true;
|
|
} else {
|
|
cmd->result = false;
|
|
}
|
|
cmd->done = true;
|
|
}
|
|
break;
|
|
case COMMAND_RUN_JS: {
|
|
doEvents();
|
|
int wv = cmd->args[0].toInt();
|
|
QString js = cmd->args[1].toString();
|
|
if (_views.contains(wv)) {
|
|
WebviewWindow *w = _views[wv];
|
|
DEBUG1("Running %s\n", js.toUtf8().constData());
|
|
w->runJs(js);
|
|
cmd->result = true;
|
|
} else {
|
|
cmd->result = false;
|
|
}
|
|
cmd->done = true;
|
|
}
|
|
break;
|
|
case COMMAND_CALL_JS: {
|
|
doEvents();
|
|
int wv = cmd->args[0].toInt();
|
|
QString js = cmd->args[1].toString();
|
|
if (_views.contains(wv)) {
|
|
WebviewWindow *w = _views[wv];
|
|
w->callJs(js, cmd);
|
|
} else {
|
|
cmd->result = false;
|
|
cmd->js_result_ok = false;
|
|
cmd->done = true;
|
|
}
|
|
}
|
|
break;
|
|
case COMMAND_DEV_TOOLS: {
|
|
doEvents();
|
|
int wv = cmd->args[0].toInt();
|
|
if (_views.contains(wv)) {
|
|
WebviewWindow *w = _views[wv];
|
|
w->openDevTools();
|
|
cmd->result = true;
|
|
} else {
|
|
cmd->result = false;
|
|
}
|
|
cmd->done = true;
|
|
}
|
|
break;
|
|
case COMMAND_SHOW_WIN:
|
|
case COMMAND_HIDE_WIN:
|
|
case COMMAND_PRESENT_WIN:
|
|
case COMMAND_MAX_WIN:
|
|
case COMMAND_MIN_WIN:
|
|
case COMMAND_SHOW_NORMAL_WIN:
|
|
{
|
|
doEvents();
|
|
int wv = cmd->args[0].toInt();
|
|
if (_views.contains(wv)) {
|
|
WebviewWindow *w = _views[wv];
|
|
int c = cmd->cmd;
|
|
if (c == COMMAND_SHOW_WIN) w->show();
|
|
else if (c == COMMAND_HIDE_WIN) w->hide();
|
|
else if (c == COMMAND_MAX_WIN) w->showMaximized();
|
|
else if (c == COMMAND_MIN_WIN) {
|
|
w->showMinimized();
|
|
}
|
|
else if (c == COMMAND_SHOW_NORMAL_WIN) w->showNormal();
|
|
else if (c == COMMAND_PRESENT_WIN) {
|
|
w->show();
|
|
w->raise();
|
|
w->activateWindow();
|
|
}
|
|
cmd->result = true;
|
|
} else {
|
|
cmd->result = false;
|
|
}
|
|
cmd->done = true;
|
|
}
|
|
break;
|
|
case COMMAND_WINDOW_STATUS:
|
|
{
|
|
doEvents();
|
|
int wv = cmd->args[0].toInt();
|
|
window_state_t ws = window_state_t::invalid;
|
|
if (_views.contains(wv)) {
|
|
WebviewWindow *w = _views[wv];
|
|
if (w->isHidden()) {
|
|
ws = window_state_t::hidden;
|
|
} else if (w->isMinimized()) {
|
|
ws = window_state_t::minimized;
|
|
} else if (w->isMaximized()) {
|
|
if (w->isActiveWindow()) {
|
|
ws = window_state_t::maximized_active;
|
|
} else {
|
|
ws = window_state_t::maximized;
|
|
}
|
|
} else if (w->isVisible()) {
|
|
if (w->isActiveWindow()) {
|
|
ws = window_state_t::normal_active;
|
|
} else {
|
|
ws = window_state_t::normal;
|
|
}
|
|
}
|
|
}
|
|
cmd->result = static_cast<int>(ws);
|
|
cmd->done = true;
|
|
}
|
|
break;
|
|
case COMMAND_MOVE: {
|
|
doEvents();
|
|
int wv = cmd->args[0].toInt();
|
|
int x = cmd->args[1].toInt();
|
|
int y = cmd->args[2].toInt();
|
|
if (_views.contains(wv)) {
|
|
WebviewWindow *w = _views[wv];
|
|
int move_count = w->moveCount();
|
|
w->move(x, y);
|
|
while (w->moveCount() == move_count) {
|
|
doEvents();
|
|
}
|
|
cmd->result = true;
|
|
} else {
|
|
cmd->result = false;
|
|
}
|
|
cmd->done = true;
|
|
}
|
|
break;
|
|
case COMMAND_RESIZE: {
|
|
doEvents();
|
|
int wv = cmd->args[0].toInt();
|
|
int width = cmd->args[1].toInt();
|
|
int height = cmd->args[2].toInt();
|
|
if (_views.contains(wv)) {
|
|
WebviewWindow *w = _views[wv];
|
|
int resize_count = w->resizeCount();
|
|
w->resize(width, height);
|
|
while (w->resizeCount() == resize_count) {
|
|
doEvents();
|
|
}
|
|
cmd->result = true;
|
|
} else {
|
|
cmd->result = false;
|
|
}
|
|
cmd->done = true;
|
|
}
|
|
break;
|
|
case COMMAND_CHOOSE_DIR: {
|
|
int wv = cmd->args[0].toInt();
|
|
QString title = cmd->args[1].toString();
|
|
QString base_dir = cmd->args[2].toString();
|
|
if (_views.contains(wv)) {
|
|
result_t r = fileDlg(wv, title, base_dir, "",
|
|
QFileDialog::Directory, QFileDialog::AcceptOpen,
|
|
"choose-dir-ok", "choose-dir-cancel"
|
|
);
|
|
cmd->js_result_ok = true;
|
|
cmd->result = (r == result_t::oke);
|
|
} else {
|
|
cmd->js_result_ok = false;
|
|
cmd->result = false;
|
|
}
|
|
cmd->done = true;
|
|
}
|
|
break;
|
|
case COMMAND_FILE_OPEN: {
|
|
int wv = cmd->args[0].toInt();
|
|
QString title = cmd->args[1].toString();
|
|
QString base_dir = cmd->args[2].toString();
|
|
QString exts = cmd->args[3].toString();
|
|
if (_views.contains(wv)) {
|
|
result_t r = fileDlg(wv, title, base_dir, exts,
|
|
QFileDialog::ExistingFile, QFileDialog::AcceptOpen,
|
|
"file-open-ok", "file-open-cancel"
|
|
);
|
|
cmd->js_result_ok = true;
|
|
cmd->result = (r == result_t::oke);
|
|
} else {
|
|
cmd->js_result_ok = true;
|
|
cmd->result = false;
|
|
}
|
|
cmd->done = true;
|
|
}
|
|
break;
|
|
case COMMAND_FILE_SAVE: {
|
|
int wv = cmd->args[0].toInt();
|
|
QString title = cmd->args[1].toString();
|
|
QString base_dir = cmd->args[2].toString();
|
|
QString exts = cmd->args[3].toString();
|
|
if (_views.contains(wv)) {
|
|
result_t r = fileDlg(wv, title, base_dir, exts,
|
|
QFileDialog::AnyFile, QFileDialog::AcceptSave,
|
|
"file-open-ok", "file-open-cancel"
|
|
);
|
|
cmd->js_result_ok = true;
|
|
cmd->result = (r == result_t::oke);
|
|
} else {
|
|
cmd->js_result_ok = true;
|
|
cmd->result = false;
|
|
}
|
|
cmd->done = true;
|
|
}
|
|
break;
|
|
case COMMAND_MESSAGE: {
|
|
int wv = cmd->args[0].toInt();
|
|
QString title = cmd->args[1].toString();
|
|
QString msg = cmd->args[2].toString();
|
|
QString submsg = cmd->args[3].toString();
|
|
rkt_messagetype_t type = static_cast<rkt_messagetype_t>(cmd->args[4].toInt());
|
|
|
|
if (_views.contains(wv)) {
|
|
msgBox(wv, title, msg, submsg, type);
|
|
cmd->js_result_ok = true;
|
|
cmd->result = true;
|
|
} else {
|
|
cmd->js_result_ok = true;
|
|
cmd->result = false;
|
|
}
|
|
cmd->done = true;
|
|
}
|
|
break;
|
|
default: {
|
|
cmd->result = false;
|
|
cmd->done = true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Rktwebview_qt::removeView(int id)
|
|
{
|
|
if (_views.contains(id)) {
|
|
//WebviewWindow *win = _views[id];
|
|
_views.remove(id);
|
|
_view_js_callbacks.remove(id);
|
|
}
|
|
}
|
|
|
|
int Rktwebview_qt::nextHandle()
|
|
{
|
|
int h = ++_current_handle;
|
|
return h;
|
|
}
|
|
|
|
rkt_wv_context_t Rktwebview_qt::newContext(const QString &boilerplate_js, bool has_pem, const QString &optional_server_cert_pem)
|
|
{
|
|
Command c(COMMAND_NEW_CONTEXT);
|
|
|
|
c.args.push_back(boilerplate_js);
|
|
c.args.push_back(has_pem);
|
|
c.args.push_back(optional_server_cert_pem);
|
|
|
|
postCommand(&c);
|
|
|
|
while(!c.done) { doEvents(); }
|
|
|
|
int id = c.result.toInt();
|
|
return id;
|
|
}
|
|
|
|
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);
|
|
|
|
postCommand(&c);
|
|
|
|
while(!c.done) { doEvents(); }
|
|
|
|
int id = c.result.toInt();
|
|
return id;
|
|
}
|
|
|
|
void Rktwebview_qt::rktWebViewClose(int wv)
|
|
{
|
|
Command c(COMMAND_CLOSE);
|
|
c.args.push_back(wv);
|
|
|
|
postCommand(&c);
|
|
|
|
while(!c.done) { doEvents(); }
|
|
}
|
|
|
|
void Rktwebview_qt::rktSetOUToken(rktwebview_t wv, const char *ou_token)
|
|
{
|
|
Command c(COMMAND_SET_OU_TOKEN);
|
|
c.args.push_back(wv);
|
|
QString ou_tok(ou_token);
|
|
c.args.push_back(ou_tok);
|
|
|
|
postCommand(&c);
|
|
|
|
while(!c.done) { doEvents(); }
|
|
}
|
|
|
|
result_t Rktwebview_qt::rktSetUrl(rktwebview_t wv, const char *url)
|
|
{
|
|
Command c(COMMAND_SET_URL);
|
|
QString _url(url);
|
|
c.args.push_back(wv);
|
|
c.args.push_back(_url);
|
|
postCommand(&c);
|
|
|
|
while(!c.done) { doEvents(); }
|
|
|
|
bool r = c.result.toBool();
|
|
|
|
return r ? result_t::oke : result_t::set_navigate_failed;
|
|
}
|
|
|
|
result_t Rktwebview_qt::rktSetHtml(rktwebview_t wv, const char *html)
|
|
{
|
|
Command c(COMMAND_SET_HTML);
|
|
QString _html(html);
|
|
c.args.push_back(wv);
|
|
c.args.push_back(_html);
|
|
postCommand(&c);
|
|
|
|
while(!c.done) { doEvents(); }
|
|
|
|
bool r = c.result.toBool();
|
|
|
|
return r ? result_t::oke : result_t::set_html_failed;
|
|
}
|
|
|
|
rkt_data_t *Rktwebview_qt::rktCallJs(rktwebview_t wv, const char *js)
|
|
{
|
|
DEBUG1("calljs: %s\n", js);
|
|
|
|
Command c(COMMAND_CALL_JS);
|
|
QString _js(js);
|
|
c.args.push_back(wv);
|
|
c.args.push_back(_js);
|
|
postCommand(&c);
|
|
while(!c.done) { doEvents(); }
|
|
|
|
rkt_data_t *r = static_cast<rkt_data_t *>(malloc(sizeof(rkt_data_t)));
|
|
r->kind = js_result;
|
|
r->data.js_result.result = c.js_result_ok ? result_t::oke : result_t::eval_js_failed;
|
|
r->data.js_result.value = copyString(c.result.toString().toUtf8());
|
|
DEBUG1("js-result: %s\n", r->data.js_result.value);
|
|
|
|
return r;
|
|
}
|
|
|
|
result_t Rktwebview_qt::rktRunJs(rktwebview_t wv, const QString &js)
|
|
{
|
|
DEBUG1("rktRunJs: %s\n", js.toUtf8().constData());
|
|
Command c(COMMAND_RUN_JS);
|
|
c.args.push_back(wv);
|
|
c.args.push_back(js);
|
|
postCommand(&c);
|
|
while(!c.done) { doEvents(); }
|
|
bool r = c.result.toBool();
|
|
return r ? result_t::oke : result_t::eval_js_failed;
|
|
}
|
|
|
|
result_t Rktwebview_qt::rktMove(rktwebview_t wv, int x, int y)
|
|
{
|
|
Command c(COMMAND_MOVE);
|
|
c.args.push_back(wv);
|
|
c.args.push_back(x);
|
|
c.args.push_back(y);
|
|
postCommand(&c);
|
|
while(!c.done) { doEvents(); }
|
|
bool r = c.result.toBool();
|
|
return r ? result_t::oke : result_t::move_failed;
|
|
}
|
|
|
|
result_t Rktwebview_qt::rktResize(rktwebview_t wv, int w, int h)
|
|
{
|
|
Command c(COMMAND_RESIZE);
|
|
c.args.push_back(wv);
|
|
c.args.push_back(w);
|
|
c.args.push_back(h);
|
|
postCommand(&c);
|
|
while(!c.done) { doEvents(); }
|
|
bool r = c.result.toBool();
|
|
return r ? result_t::oke : result_t::resize_failed;
|
|
}
|
|
|
|
result_t Rktwebview_qt::rktHideWindow(rktwebview_t w)
|
|
{
|
|
return doWindow(w, COMMAND_HIDE_WIN, result_t::failed);
|
|
}
|
|
|
|
result_t Rktwebview_qt::rktShowWindow(rktwebview_t w)
|
|
{
|
|
return doWindow(w, COMMAND_SHOW_WIN, result_t::failed);
|
|
}
|
|
|
|
result_t Rktwebview_qt::rktPresentWindow(rktwebview_t w)
|
|
{
|
|
return doWindow(w, COMMAND_PRESENT_WIN, result_t::failed);
|
|
}
|
|
|
|
result_t Rktwebview_qt::rktMaximizeWindow(rktwebview_t w)
|
|
{
|
|
return doWindow(w, COMMAND_MAX_WIN, result_t::failed);
|
|
}
|
|
|
|
result_t Rktwebview_qt::rktMinimizeWindow(rktwebview_t w)
|
|
{
|
|
return doWindow(w, COMMAND_MIN_WIN, result_t::failed);
|
|
}
|
|
|
|
result_t Rktwebview_qt::rktShowNormalWindow(rktwebview_t w)
|
|
{
|
|
return doWindow(w, COMMAND_SHOW_NORMAL_WIN, result_t::failed);
|
|
}
|
|
|
|
window_state_t Rktwebview_qt::rktWindowState(rktwebview_t w)
|
|
{
|
|
Command c(COMMAND_WINDOW_STATUS);
|
|
c.args.push_back(w);
|
|
postCommand(&c);
|
|
while(!c.done) { doEvents(); }
|
|
int r = c.result.toInt();
|
|
window_state_t ws = static_cast<window_state_t>(r);
|
|
return ws;
|
|
}
|
|
|
|
void Rktwebview_qt::rktQuit()
|
|
{
|
|
Command c(COMMAND_QUIT);
|
|
postCommand(&c);
|
|
|
|
while(!c.done) { doEvents(); }
|
|
}
|
|
|
|
result_t Rktwebview_qt::fileDlg(rktwebview_t w,
|
|
const QString &title,
|
|
const QString &base,
|
|
const QString &filters,
|
|
QFileDialog::FileMode mode,
|
|
QFileDialog::AcceptMode am,
|
|
QString evt_ok,
|
|
QString evt_cancel)
|
|
{
|
|
if (_views.contains(w)) {
|
|
WebviewWindow *win = _views[w];
|
|
|
|
QFileDialog *dlg = new QFileDialog(win, title, base);
|
|
dlg->setFileMode(mode);
|
|
dlg->setAcceptMode(am);
|
|
|
|
QString pe = filters;
|
|
QStringList p = pe.split(";;");
|
|
|
|
dlg->setNameFilters(p);
|
|
dlg->setProperty("rktwebview", w);
|
|
dlg->setProperty("evt-ok", evt_ok);
|
|
dlg->setProperty("evt-cancel", evt_cancel);
|
|
|
|
connect(dlg, &QFileDialog::accepted, this, [this]() {
|
|
QFileDialog *dlg = static_cast<QFileDialog *>(sender());
|
|
|
|
rktwebview_t w = dlg->property("rktwebview").toInt();
|
|
QString evt_ok = dlg->property("evt-ok").toString();
|
|
|
|
QStringList l = dlg->selectedFiles();
|
|
QString file;
|
|
if (l.size() > 0) {
|
|
file = l[0];
|
|
}
|
|
|
|
EventContainer e(evt_ok);
|
|
e["choosen"] = file;
|
|
e["filter"] = dlg->selectedNameFilter();
|
|
e["dir"] = dlg->directory().path();
|
|
|
|
this->triggerEvent(w, e);
|
|
});
|
|
|
|
connect(dlg, &QDialog::rejected, this, [this]() {
|
|
QFileDialog *dlg = static_cast<QFileDialog *>(sender());
|
|
|
|
rktwebview_t w = dlg->property("rktwebview").toInt();
|
|
QString evt_cancel = dlg->property("evt-cancel").toString();
|
|
|
|
EventContainer e(evt_cancel);
|
|
this->triggerEvent(w, e);
|
|
});
|
|
dlg->show();
|
|
return result_t::oke;
|
|
} else {
|
|
return result_t::failed;
|
|
}
|
|
}
|
|
|
|
result_t Rktwebview_qt::rktChooseDir(rktwebview_t w, const char *title, const char *base_dir)
|
|
{
|
|
Command c(COMMAND_CHOOSE_DIR);
|
|
c.args.push_back(w);
|
|
QString s_title(title);
|
|
c.args.push_back(s_title);
|
|
QString s_base_dir(base_dir);
|
|
c.args.push_back(s_base_dir);
|
|
|
|
postCommand(&c);
|
|
while(!c.done) {
|
|
doEvents();
|
|
}
|
|
|
|
bool r = c.result.toBool();
|
|
return r ? result_t::oke : result_t::resize_failed;
|
|
}
|
|
|
|
result_t Rktwebview_qt::rktFileOpen(rktwebview_t w, const char *title, const char *base_dir, const char *permitted_exts)
|
|
{
|
|
Command c(COMMAND_FILE_OPEN);
|
|
c.args.push_back(w);
|
|
QString s_title(title);
|
|
c.args.push_back(s_title);
|
|
QString s_base_dir(base_dir);
|
|
c.args.push_back(s_base_dir);
|
|
QString s_pe(permitted_exts);
|
|
c.args.push_back(s_pe);
|
|
|
|
postCommand(&c);
|
|
while(!c.done) {
|
|
doEvents();
|
|
}
|
|
|
|
bool r = c.result.toBool();
|
|
return r ? result_t::oke : result_t::resize_failed;
|
|
}
|
|
|
|
result_t Rktwebview_qt::rktFileSave(rktwebview_t w, const char *title, const char *base_dir, const char *permitted_exts)
|
|
{
|
|
Command c(COMMAND_FILE_SAVE);
|
|
c.args.push_back(w);
|
|
QString s_title(title);
|
|
c.args.push_back(s_title);
|
|
QString s_base_dir(base_dir);
|
|
c.args.push_back(s_base_dir);
|
|
QString s_pe(permitted_exts);
|
|
c.args.push_back(s_pe);
|
|
|
|
postCommand(&c);
|
|
while(!c.done) {
|
|
doEvents();
|
|
}
|
|
|
|
bool r = c.result.toBool();
|
|
return r ? result_t::oke : result_t::resize_failed;
|
|
}
|
|
|
|
result_t Rktwebview_qt::rktWindowSetTitle(rktwebview_t wv, const char *title)
|
|
{
|
|
Command c(COMMAND_SET_TITLE);
|
|
c.args.push_back(wv);
|
|
c.args.push_back(title);
|
|
postCommand(&c);
|
|
while(!c.done) { doEvents(); }
|
|
bool r = c.result.toBool();
|
|
return r ? result_t::oke : result_t::failed;
|
|
}
|
|
|
|
void Rktwebview_qt::msgBox(rktwebview_t w, const QString &title, const QString &message, const QString &submessage, rkt_messagetype_t type)
|
|
{
|
|
|
|
WebviewWindow *win = _views[w];
|
|
QMessageBox *msg = new QMessageBox(win);
|
|
|
|
auto prepButton = [msg, w, this](QMessageBox::StandardButton b, QString event) {
|
|
msg->addButton(b);
|
|
QAbstractButton *btn = msg->button(b);
|
|
QVariant v = QVariant::fromValue(msg);
|
|
btn->setProperty("messagebox", v);
|
|
btn->setProperty("event", event);
|
|
btn->setProperty("rktwebview", w);
|
|
connect(btn, &QAbstractButton::clicked, this, [this]() {
|
|
QAbstractButton *btn = static_cast<QAbstractButton *>(sender());
|
|
QMessageBox *mb = btn->property("messagebox").value<QMessageBox *>();
|
|
rktwebview_t w = btn->property("rktwebview").toInt();
|
|
|
|
EventContainer e(btn->property("event").toString());
|
|
this->triggerEvent(w, e);
|
|
|
|
mb->close();
|
|
});
|
|
};
|
|
|
|
msg->setWindowTitle(title);
|
|
|
|
QString m = QString("<p>") + message + "</p>";
|
|
m += QString::asprintf("<p><small>%s</small></p>", submessage.toUtf8().constData());
|
|
msg->setInformativeText(m);
|
|
|
|
QMessageBox::Icon icn;
|
|
|
|
if (type == rkt_messagetype_t::error) {
|
|
icn = QMessageBox::Icon::Critical;
|
|
prepButton(QMessageBox::StandardButton::Ok, "msgbox-ok");
|
|
} else if (type == rkt_messagetype_t::warning) {
|
|
icn = QMessageBox::Icon::Warning;
|
|
prepButton(QMessageBox::StandardButton::Ok, "msgbox-ok");
|
|
} else if (type == rkt_messagetype_t::yes_no || type == rkt_messagetype_t::oke_cancel) {
|
|
icn = QMessageBox::Icon::Question;
|
|
if (type == rkt_messagetype_t::yes_no) {
|
|
prepButton(QMessageBox::StandardButton::No, "msgbox-no");
|
|
prepButton(QMessageBox::StandardButton::Yes, "msgbox-yes");
|
|
} else {
|
|
prepButton(QMessageBox::StandardButton::Cancel, "msgbox-cancel");
|
|
prepButton(QMessageBox::StandardButton::Ok, "msgbox-ok");
|
|
}
|
|
} else { // informatio
|
|
icn = QMessageBox::Icon::Information;
|
|
prepButton(QMessageBox::StandardButton::Ok, "msgbox-ok");
|
|
}
|
|
msg->setIcon(icn);
|
|
msg->show();
|
|
}
|
|
|
|
result_t Rktwebview_qt::rktMessageBox(rktwebview_t w, const char *title, const char *message, const char *submessage, rkt_messagetype_t type)
|
|
{
|
|
Command c(COMMAND_MESSAGE);
|
|
c.args.push_back(w);
|
|
QString s_title(title);
|
|
c.args.push_back(s_title);
|
|
QString s_msg(message);
|
|
c.args.push_back(s_msg);
|
|
QString s_submsg(submessage);
|
|
c.args.push_back(s_submsg);
|
|
c.args.push_back(static_cast<int>(type));
|
|
|
|
postCommand(&c);
|
|
|
|
while(!c.done) {
|
|
doEvents();
|
|
}
|
|
|
|
bool r = c.result.toBool();
|
|
return r ? result_t::oke : result_t::failed;
|
|
}
|
|
|
|
result_t Rktwebview_qt::doWindow(rktwebview_t w, int cmd, result_t on_error)
|
|
{
|
|
Command c(cmd);
|
|
c.args.push_back(w);
|
|
postCommand(&c);
|
|
while(!c.done) { doEvents(); }
|
|
bool r = c.result.toBool();
|
|
return r ? result_t::oke : on_error;
|
|
}
|
|
|
|
bool Rktwebview_qt::rktValid(rktwebview_t wv)
|
|
{
|
|
return _views.contains(wv);
|
|
}
|
|
|
|
result_t Rktwebview_qt::rktOpenDevtools(rktwebview_t wv)
|
|
{
|
|
Command c(COMMAND_DEV_TOOLS);
|
|
c.args.push_back(wv);
|
|
postCommand(&c);
|
|
while(!c.done) { doEvents(); }
|
|
bool r = c.result.toBool();
|
|
return r ? result_t::oke : result_t::eval_js_failed;
|
|
}
|
|
|
|
void Rktwebview_qt::onPageLoad(rktwebview_t w)
|
|
{
|
|
}
|
|
|
|
void Rktwebview_qt::pageLoaded(rktwebview_t w, bool ok)
|
|
{
|
|
if (!ok) {
|
|
// Inject code of the profile to this page
|
|
WebviewWindow *win = _views[w];
|
|
|
|
if (win->navigationEventSent()) {
|
|
return;
|
|
}
|
|
|
|
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");
|
|
e["oke"] = ok;
|
|
triggerEvent(w, e);
|
|
}
|
|
|
|
void Rktwebview_qt::triggerEvent(rktwebview_t wv, const EventContainer &e)
|
|
{
|
|
triggerEvent(wv, mkEventJson(e));
|
|
}
|
|
|
|
void Rktwebview_qt::triggerEvent(rktwebview_t wv, const QString &msg)
|
|
{
|
|
if (_view_js_callbacks.contains(wv)) {
|
|
event_cb_t js_event_cb = _view_js_callbacks[wv];
|
|
char *evt = copyString(msg.toUtf8().constData());
|
|
rkt_data_t *d = static_cast<rkt_data_t *>(malloc(sizeof(rkt_data_t)));
|
|
d->kind = rkt_data_kind_t::event;
|
|
d->data.event.wv = wv;
|
|
d->data.event.event = evt;
|
|
js_event_cb(d);
|
|
}
|
|
}
|
|
|
|
void Rktwebview_qt::execApp()
|
|
{
|
|
_app->exec();
|
|
}
|
|
|
|
QApplication *Rktwebview_qt::app()
|
|
{
|
|
return _app;
|
|
}
|
|
|
|
void Rktwebview_qt::deleteApp()
|
|
{
|
|
delete _app;
|
|
_app = nullptr;
|
|
}
|
|
|
|
void Rktwebview_qt::initApp()
|
|
{
|
|
_app = new QApplication(_argc, _argv);
|
|
_app->setQuitOnLastWindowClosed(false);
|
|
|
|
// See Qt 6.10 remark at doEvents.
|
|
//connect(&_evt_loop_timer, &QTimer::timeout, this, &Rktwebview_qt::stopEventloop);
|
|
|
|
// Because we are using processEvents only (Qt 6.10), we need this dispatcher to
|
|
// handle deferred Deletes.
|
|
|
|
/*const auto *eventDispatcher = QThread::currentThread()->eventDispatcher();
|
|
QObject::connect(eventDispatcher, &QAbstractEventDispatcher::aboutToBlock,
|
|
QThread::currentThread(), []{
|
|
if (QThread::currentThread()->loopLevel() == 0)
|
|
QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
|
|
}
|
|
);
|
|
*/
|
|
}
|
|
|
|
|
|
void Rktwebview_qt::runJs(rktwebview_t wv, const char *js)
|
|
{
|
|
if (_views.contains(wv)) {
|
|
QString _js(js);
|
|
WebviewWindow *win = _views[wv];
|
|
win->runJs(_js);
|
|
}
|
|
}
|
|
|
|
|
|
void Rktwebview_qt::postCommand(Command *cmd)
|
|
{
|
|
CommandEvent *e = new CommandEvent(cmd);
|
|
QApplication::postEvent(this, e);
|
|
}
|
|
|
|
void Rktwebview_qt::handleCommandEvent(CommandEvent *e)
|
|
{
|
|
Command *c = e->cmd();
|
|
processCommand(c);
|
|
}
|
|
|
|
void Rktwebview_qt::customEvent(QEvent *event)
|
|
{
|
|
if (event->type() == COMMAND_EVENT) {
|
|
handleCommandEvent(static_cast<CommandEvent *>(event));
|
|
}
|
|
}
|
|
|
|
void Rktwebview_qt::doEvents()
|
|
{
|
|
//QTime ct = QTime::currentTime();
|
|
//QTime expire = QTime::currentTime().addMSecs(2);
|
|
//while(QTime::currentTime() <= expire) {
|
|
//_app->processEvents();
|
|
//}
|
|
|
|
// Qt 6.10 --> this leads to a core dump
|
|
// together with the stopEventloop stuff.
|
|
// Qt 6.4 seem stable.
|
|
/*if (_evt_loop_depth == 0) {
|
|
_evt_loop_depth += 1;
|
|
_evt_loop_timer.setSingleShot(true);
|
|
_evt_loop_timer.start(2);
|
|
//_evt_loop.exec();
|
|
_app->exec();
|
|
}*/
|
|
QThread::usleep(500);
|
|
}
|
|
|
|
void Rktwebview_qt::stopEventloop()
|
|
{
|
|
//_evt_loop.exit(0);
|
|
//_app->exit(0);
|
|
//_evt_loop_depth -= 1;
|
|
}
|
|
|
|
|
|
|
|
Rktwebview_qt::Rktwebview_qt(int argc, char *argv[]) : QObject()
|
|
{
|
|
_context_counter = 0;
|
|
_current_handle = 0;
|
|
_evt_loop_depth = 0;
|
|
|
|
_argc = argc;
|
|
_argv = argv;
|
|
|
|
_app = 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;
|
|
}
|
|
|
|
delete _app;
|
|
}
|