From d54105be89a3d81cc89774b9ef60b740e3eea2ea Mon Sep 17 00:00:00 2001 From: Hans Dijkema Date: Fri, 17 Oct 2025 22:54:46 +0200 Subject: [PATCH 1/5] Fix problems with navigation events that aren't generated by GtkWebKit. (used lib: libwebkit2gtk-4.1-0 on linux Mint). The WebKit engine to apparently doesn't catch the navigations and does not produce navigation events, when something like "webui_bind(_webui_win, "", webui_event_handler);" is called. Although this should be expected behaviour. By implementing a handler for the GtkWebKit decision signal, this navigation event can still be generated. Signed-off-by: Hans Dijkema --- include/webui.h | 25 ++++++++++ src/webui.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 142 insertions(+), 2 deletions(-) diff --git a/include/webui.h b/include/webui.h index 2d5884a2f..2aeae82fd 100644 --- a/include/webui.h +++ b/include/webui.h @@ -523,6 +523,31 @@ WEBUI_EXPORT void webui_set_browser_folder(const char* path); */ WEBUI_EXPORT bool webui_set_default_root_folder(const char* path); +/** + * @brief Set a callback function on the given window to allow navigations. + * Must return false if navigation is not allowed, true, otherwise. + * + * A 'NULL' navigation handler will remove the current navigation_handler. + * + * NB. This currently works only for GtkWebKit WebView implementations. + * Because GtkWebKit allways navigates, i.e. doesnt react to + * webui_bind(_webui_win, "", webui_event_handler) which is supposed to + * catch navigation requests. + * + * Also, although bind(win, "", evt_handler) does not work for GtkWebKit, + * this implementation will always fire an event on navigation, unless + * mayNavigate() returns true (which is the default, when the callback is not set). + * + * @example + * bool mayNavigate(size_t window) + * { + * if (_just_called_show_with_url) { return true; } + * else { return false; } // expect a navigation event to be handled + * } + */ +WEBUI_EXPORT void webui_set_navigation_handler(size_t window, bool (*may_navigate_handler)(size_t window)); + + /** * @brief Set a callback to catch the close event of the WebView window. * Must return `false` to prevent the close event, `true` otherwise. diff --git a/src/webui.c b/src/webui.c index d7e1c612a..2c6943763 100644 --- a/src/webui.c +++ b/src/webui.c @@ -8,6 +8,8 @@ Canada. */ +#define webui_log_debug printf + // 64Mb max dynamic memory allocation #define WEBUI_MAX_BUF (64000000) @@ -247,10 +249,18 @@ typedef struct webui_event_inf_t { typedef void *(*webkit_web_view_new_func)(void); typedef void (*webkit_web_view_load_uri_func)(void *, const char *); typedef const char *(*webkit_web_view_get_title_func)(void *); + typedef void (*webkit_1ptr_arg_func)(void *); + typedef int (*webkit_1ptr_arg_2int_func)(void *); + typedef void *(*webkit_1ptr_arg_2ptr_func)(void *); + typedef const char *(*webkit_1ptr_arg_2str_func)(void *); static webkit_web_view_new_func webkit_web_view_new = NULL; static webkit_web_view_load_uri_func webkit_web_view_load_uri = NULL; static webkit_web_view_get_title_func webkit_web_view_get_title = NULL; - + static webkit_1ptr_arg_func webkit_policy_decision_ignore = NULL; + static webkit_1ptr_arg_2int_func webkit_navigation_policy_decision_get_navigation_type = NULL; + static webkit_1ptr_arg_2ptr_func webkit_navigation_policy_decision_get_request = NULL; + static webkit_1ptr_arg_2str_func webkit_uri_request_get_uri = NULL; + typedef struct _webui_wv_linux_t { // Linux WebView void* gtk_win; @@ -355,6 +365,7 @@ typedef struct _webui_window_t { int x; int y; bool position_set; + bool (*may_navigate_handler)(size_t window); bool (*close_handler_wv)(size_t window); const void*(*files_handler)(const char* filename, int* length); const void*(*files_handler_window)(size_t window, const char* filename, int* length); @@ -759,6 +770,21 @@ void webui_run(size_t window, const char* script) { _webui_send_all(win, 0, WEBUI_CMD_JS_QUICK, script, js_len); } +void webui_set_navigation_handler(size_t window, bool (*may_navigate_handler)(size_t window)) { + #ifdef WEBUI_LOG + webui_log_debug("[User]webui_set_navigation_handler(%zu, %p)", window, may_navigate_handler); + #endif + + // Dereference + if (_webui_mutex_app_is_exit_now(WEBUI_MUTEX_GET_STATUS) || _webui.wins[window] == NULL) + return; + + _webui_window_t* win = _webui.wins[window]; + + // Set the navigation handler + win->may_navigate_handler = may_navigate_handler; +} + void webui_set_close_handler_wv(size_t window, bool(*close_handler)(size_t window)) { // Initialization @@ -11763,6 +11789,84 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) { } } + // Decision Event + #define WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION 0 + #define WEBKIT_NAVIGATION_TYPE_LINK_CLICKED 0 + #define WEBKIT_NAVIGATION_TYPE_FORM_SUBMITTED 1 + #define WEBKIT_NAVIGATION_TYPE_BACK_FORWARD 2 + #define WEBKIT_NAVIGATION_TYPE_RELOAD 3 + #define WEBKIT_NAVIGATION_TYPE_FORM_RESUBMITTED 4 + #define WEBKIT_NAVIGATION_TYPE_OTHER 5 + + static bool _webui_wv_event_decision(void *widget, void *decision, int decision_type, void *user_data) { + switch(decision_type) { + case WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION: { + + int navigation_type = webkit_navigation_policy_decision_get_navigation_type(decision); + bool intercept_navigation = false; + + _webui_window_t* win = _webui_dereference_win_ptr(user_data); + if (win->may_navigate_handler) { + intercept_navigation = !(win->may_navigate_handler(win->num)); + } + + if (intercept_navigation) { + webkit_policy_decision_ignore(decision); + + void *uri_request = webkit_navigation_policy_decision_get_request(decision); + const char *webkit_uri = webkit_uri_request_get_uri(uri_request); + + size_t s = strlen(webkit_uri) + 1; + char *uri = (char *) _webui_malloc(s); + strncpy(uri, webkit_uri, s - 1); + uri[s] = '\0'; + + char buf[20]; + snprintf(buf, 20, "%d", navigation_type); + size_t nt_s = strlen(buf) + 1; + char *type = (char *) _webui_malloc(nt_s); + strncpy(uri, buf, nt_s - 1); + uri[nt_s] = '\0'; + + webui_event_inf_t* event_inf = NULL; + size_t event_num = _webui_new_event_inf(win, &event_inf); + + // TODO: Not sure how this works and if the right connection_id is taken. + int connection_id = 0; + // TODO: Not sure if this is the way to get the client. + struct mg_connection* client = win->single_client; + + event_inf->client = client; + event_inf->connection_id = connection_id; + + // Event Info Extras + event_inf->event_data[0] = uri; + event_inf->event_size[0] = strlen(uri); + event_inf->event_data[1] = type; + event_inf->event_size[1] = strlen(type); + + _webui_window_event( + win, // Event -> Window + connection_id, // Event -> Client Unique ID + WEBUI_EVENT_NAVIGATION, // Event -> Type of this event + "", // Event -> HTML Element + event_num, // Event -> Event Number + _webui_client_get_id(win, client), // Event -> Client ID + _webui_get_cookies_full(client) // Event -> Full cookies + ); + + // Free event + _webui_free_event_inf(win, event_num); + + // Return that this event is handled. + return true; + } + } + break; + } + return false; + } + // Close Event static void _webui_wv_event_closed(void *widget, void *arg) { #ifdef WEBUI_LOG @@ -12095,6 +12199,14 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) { libwebkit, "webkit_web_view_load_uri"); webkit_web_view_get_title = (webkit_web_view_get_title_func)dlsym( libwebkit, "webkit_web_view_get_title"); + webkit_policy_decision_ignore = (webkit_1ptr_arg_func)dlsym( + libwebkit, "webkit_policy_decision_ignore"); + webkit_navigation_policy_decision_get_navigation_type = (webkit_1ptr_arg_2int_func)dlsym( + libwebkit, "webkit_navigation_policy_decision_get_navigation_type"); + webkit_navigation_policy_decision_get_request = (webkit_1ptr_arg_2ptr_func)dlsym( + libwebkit, "webkit_navigation_policy_decision_get_request"); + webkit_uri_request_get_uri = (webkit_1ptr_arg_2str_func)dlsym( + libwebkit, "webkit_uri_request_get_uri"); // Check GTK if ( @@ -12119,7 +12231,10 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) { } // Check WebView - if (!webkit_web_view_new || !webkit_web_view_load_uri || !webkit_web_view_get_title) { + if (!webkit_web_view_new || !webkit_web_view_load_uri || !webkit_web_view_get_title || + !webkit_policy_decision_ignore || !webkit_navigation_policy_decision_get_navigation_type || + !webkit_navigation_policy_decision_get_request || !webkit_uri_request_get_uri + ) { #ifdef WEBUI_LOG printf("[Core]\t\t_webui_load_gtk_and_webkit() -> WebKit symbol addresses failed\n"); #endif From 951af6231923f6ef6bd6a636a49983da87edaf07 Mon Sep 17 00:00:00 2001 From: Showns <116365846+AlbertShown@users.noreply.github.com> Date: Mon, 20 Oct 2025 10:16:57 -0400 Subject: [PATCH 2/5] Updating code to follow current coding style Updated the navigation handler function name and its description for clarity. --- include/webui.h | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/include/webui.h b/include/webui.h index 2aeae82fd..78c49464a 100644 --- a/include/webui.h +++ b/include/webui.h @@ -524,29 +524,17 @@ WEBUI_EXPORT void webui_set_browser_folder(const char* path); WEBUI_EXPORT bool webui_set_default_root_folder(const char* path); /** - * @brief Set a callback function on the given window to allow navigations. - * Must return false if navigation is not allowed, true, otherwise. - * - * A 'NULL' navigation handler will remove the current navigation_handler. - * - * NB. This currently works only for GtkWebKit WebView implementations. - * Because GtkWebKit allways navigates, i.e. doesnt react to - * webui_bind(_webui_win, "", webui_event_handler) which is supposed to - * catch navigation requests. - * - * Also, although bind(win, "", evt_handler) does not work for GtkWebKit, - * this implementation will always fire an event on navigation, unless - * mayNavigate() returns true (which is the default, when the callback is not set). + * @brief Set a callback to catch the navigation event of the WebView window. + * Must return `false` to prevent the navigation event, `true` otherwise. * * @example - * bool mayNavigate(size_t window) - * { - * if (_just_called_show_with_url) { return true; } - * else { return false; } // expect a navigation event to be handled + * bool myNavigationEvent(size_t window) { + * // Prevent WebView window navigation event + * return false; * } + * webui_set_navigation_handler_wv(myWindow, myNavigationEvent); */ -WEBUI_EXPORT void webui_set_navigation_handler(size_t window, bool (*may_navigate_handler)(size_t window)); - +WEBUI_EXPORT void webui_set_navigation_handler_wv(size_t window, bool (*may_navigate_handler)(size_t window)); /** * @brief Set a callback to catch the close event of the WebView window. From d7efd26cbcb4346af563b2f6c95a5baf602d3cbd Mon Sep 17 00:00:00 2001 From: Showns <116365846+AlbertShown@users.noreply.github.com> Date: Mon, 20 Oct 2025 10:36:50 -0400 Subject: [PATCH 3/5] Updating code to follow current coding style --- src/webui.c | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/src/webui.c b/src/webui.c index 2c6943763..5317a1fe3 100644 --- a/src/webui.c +++ b/src/webui.c @@ -8,8 +8,6 @@ Canada. */ -#define webui_log_debug printf - // 64Mb max dynamic memory allocation #define WEBUI_MAX_BUF (64000000) @@ -365,7 +363,7 @@ typedef struct _webui_window_t { int x; int y; bool position_set; - bool (*may_navigate_handler)(size_t window); + bool (*navigation_handler_wv)(size_t window); bool (*close_handler_wv)(size_t window); const void*(*files_handler)(const char* filename, int* length); const void*(*files_handler_window)(size_t window, const char* filename, int* length); @@ -770,9 +768,9 @@ void webui_run(size_t window, const char* script) { _webui_send_all(win, 0, WEBUI_CMD_JS_QUICK, script, js_len); } -void webui_set_navigation_handler(size_t window, bool (*may_navigate_handler)(size_t window)) { +void webui_set_navigation_handler_wv(size_t window, bool (*navigate_handler)(size_t window)) { #ifdef WEBUI_LOG - webui_log_debug("[User]webui_set_navigation_handler(%zu, %p)", window, may_navigate_handler); + webui_log_debug("[User]webui_set_navigation_handler_wv(%zu, %p)", window, navigate_handler); #endif // Dereference @@ -782,7 +780,7 @@ void webui_set_navigation_handler(size_t window, bool (*may_navigate_handler)(si _webui_window_t* win = _webui.wins[window]; // Set the navigation handler - win->may_navigate_handler = may_navigate_handler; + win->navigation_handler_wv = navigate_handler; } void webui_set_close_handler_wv(size_t window, bool(*close_handler)(size_t window)) { @@ -11806,8 +11804,8 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) { bool intercept_navigation = false; _webui_window_t* win = _webui_dereference_win_ptr(user_data); - if (win->may_navigate_handler) { - intercept_navigation = !(win->may_navigate_handler(win->num)); + if (win->navigate_handler) { + intercept_navigation = !(win->navigate_handler(win->num)); } if (intercept_navigation) { @@ -11827,18 +11825,13 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) { char *type = (char *) _webui_malloc(nt_s); strncpy(uri, buf, nt_s - 1); uri[nt_s] = '\0'; - + + // Event Info webui_event_inf_t* event_inf = NULL; size_t event_num = _webui_new_event_inf(win, &event_inf); - - // TODO: Not sure how this works and if the right connection_id is taken. - int connection_id = 0; - // TODO: Not sure if this is the way to get the client. - struct mg_connection* client = win->single_client; - - event_inf->client = client; - event_inf->connection_id = connection_id; - + event_inf->client = NULL; // This is a WebKitGTK Event, so we don't have any WebSocket client + event_inf->connection_id = 0; // This is a WebKitGTK Event, so we don't have any WebSocket connection ID + // Event Info Extras event_inf->event_data[0] = uri; event_inf->event_size[0] = strlen(uri); @@ -11851,8 +11844,8 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) { WEBUI_EVENT_NAVIGATION, // Event -> Type of this event "", // Event -> HTML Element event_num, // Event -> Event Number - _webui_client_get_id(win, client), // Event -> Client ID - _webui_get_cookies_full(client) // Event -> Full cookies + 0, // Event -> Client ID | This is a WebKitGTK Event, so we don't have any WebSocket client ID + NULL // Event -> Full cookies | TODO: Get cookies using WebKKitGTK APIs ); // Free event From 473c0f768d4ce1ef93ccffbbc2e748369ea937e9 Mon Sep 17 00:00:00 2001 From: Showns <116365846+AlbertShown@users.noreply.github.com> Date: Mon, 20 Oct 2025 10:38:58 -0400 Subject: [PATCH 4/5] Updating code to follow current coding style --- include/webui.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/webui.h b/include/webui.h index 78c49464a..e81ca03e7 100644 --- a/include/webui.h +++ b/include/webui.h @@ -534,7 +534,7 @@ WEBUI_EXPORT bool webui_set_default_root_folder(const char* path); * } * webui_set_navigation_handler_wv(myWindow, myNavigationEvent); */ -WEBUI_EXPORT void webui_set_navigation_handler_wv(size_t window, bool (*may_navigate_handler)(size_t window)); +WEBUI_EXPORT void webui_set_navigation_handler_wv(size_t window, bool (*navigate_handler)(size_t window)); /** * @brief Set a callback to catch the close event of the WebView window. From 9d3833e579eb5a486b841c764d3ab1f86fdf69fa Mon Sep 17 00:00:00 2001 From: Showns <116365846+AlbertShown@users.noreply.github.com> Date: Mon, 20 Oct 2025 10:42:58 -0400 Subject: [PATCH 5/5] Updating code to follow current coding style --- src/webui.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webui.c b/src/webui.c index 5317a1fe3..2d8c59afd 100644 --- a/src/webui.c +++ b/src/webui.c @@ -11840,7 +11840,7 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) { _webui_window_event( win, // Event -> Window - connection_id, // Event -> Client Unique ID + 0, // Event -> Client Unique ID | This is a WebKitGTK Event, so we don't have any WebSocket client unique ID WEBUI_EVENT_NAVIGATION, // Event -> Type of this event "", // Event -> HTML Element event_num, // Event -> Event Number