changeset 2979:b59103280114

GTK3: Support for dw_html_javascript_add() and DW_SIGNAL_HTML_MESSAGE.
author bsmith@81767d24-ef19-dc11-ae90-00e081727c95
date Sat, 25 Mar 2023 01:49:52 +0000
parents d84182f0054e
children 7e273fec75ae
files gtk3/dw.c
diffstat 1 files changed, 152 insertions(+), 18 deletions(-) [+]
line wrap: on
line diff
--- a/gtk3/dw.c	Fri Mar 24 23:04:39 2023 +0000
+++ b/gtk3/dw.c	Sat Mar 25 01:49:52 2023 +0000
@@ -171,6 +171,7 @@
 static gint _dw_switch_page_event(GtkNotebook *notebook, GtkWidget *page, guint page_num, gpointer data);
 static gint _dw_column_click_event(GtkWidget *widget, gpointer data);
 static void _dw_html_result_event(GObject *object, GAsyncResult *result, gpointer script_data);
+static void _dw_html_message_event(WebKitUserContentManager *manager, WebKitJavascriptResult *result, gpointer *data);
 #ifdef USE_WEBKIT
 #ifdef USE_WEBKIT2
 static void _dw_html_changed_event(WebKitWebView  *web_view, WebKitLoadEvent load_event, gpointer data);
@@ -205,10 +206,8 @@
 
 } DWSignalHandler;
 
-#define SIGNALMAX 20
-
-/* A list of signal forwarders, to account for paramater differences. */
-static DWSignalList DWSignalTranslate[SIGNALMAX] = {
+/* A list of signal forwarders, to account for parameter differences. */
+static DWSignalList DWSignalTranslate[] = {
    { _dw_configure_event,         DW_SIGNAL_CONFIGURE },
    { _dw_key_press_event,         DW_SIGNAL_KEY_PRESS },
    { _dw_button_press_event,      DW_SIGNAL_BUTTON_PRESS },
@@ -232,7 +231,9 @@
 #else
    { _dw_generic_event,           DW_SIGNAL_HTML_CHANGED },
 #endif
-   { _dw_html_result_event,       DW_SIGNAL_HTML_RESULT }
+   { _dw_html_result_event,       DW_SIGNAL_HTML_RESULT },
+   { _dw_html_message_event,      DW_SIGNAL_HTML_MESSAGE },
+   { NULL,                        "" }
 };
 
 /* Alignment flags */
@@ -1127,16 +1128,18 @@
 }
 
 /* Finds the translation function for a given signal name */
-static void *_dw_findsigfunc(const char *signame)
-{
-   int z;
-
-   for(z=0;z<SIGNALMAX;z++)
+static DWSignalList _dw_findsignal(const char *signame)
+{
+   int z=0;
+   static DWSignalList empty = {0};
+
+   while(DWSignalTranslate[z].func)
    {
       if(strcasecmp(signame, DWSignalTranslate[z].name) == 0)
-         return DWSignalTranslate[z].func;
-   }
-   return NULL;
+         return DWSignalTranslate[z];
+      z++;
+   }
+   return empty;
 }
 
 static DWSignalHandler _dw_get_signal_handler(gpointer data)
@@ -1299,6 +1302,68 @@
 #endif
 }
 
+#ifdef USE_WEBKIT2
+static void _dw_html_message_event(WebKitUserContentManager *manager, WebKitJavascriptResult *js_result, gpointer *data)
+{
+    HWND window = (HWND)data[0];
+    int (*htmlmessagefunc)(HWND, char *, char *, void *) = NULL;
+    void *user_data = NULL;
+    gchar *name = (gchar *)data[1];
+    gint handlerdata;
+#if WEBKIT_CHECK_VERSION(2, 22, 0)
+    JSCValue *value;
+#else
+    JSValueRef value;
+    JSGlobalContextRef context;
+#endif
+
+    if(window && (handlerdata = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(window), "_dw_html_message_id"))))
+    {
+        void *params[3] = { GINT_TO_POINTER(handlerdata-1), 0, window };
+        DWSignalHandler work = _dw_get_signal_handler(params);
+
+        if(work.window)
+        {
+            htmlmessagefunc = work.func;
+            user_data = work.data;
+        }
+    }
+
+#if WEBKIT_CHECK_VERSION(2, 22, 0)
+    value = webkit_javascript_result_get_js_value(js_result);
+    if(jsc_value_is_string(value))
+    {
+        gchar *str_value = jsc_value_to_string(value);
+        JSCException *exception = jsc_context_get_exception(jsc_value_get_context(value));
+#else
+    context = webkit_javascript_result_get_global_context(js_result);
+    value = webkit_javascript_result_get_value(js_result);
+    if (JSValueIsString(context, value))
+    {
+        JSStringRef js_str_value;
+        gchar *str_value;
+        gsize str_length;
+
+        js_str_value = JSValueToStringCopy(context, value, NULL);
+        str_length = JSStringGetMaximumUTF8CStringSize(js_str_value);
+        str_value = (gchar *)g_malloc (str_length);
+        JSStringGetUTF8CString(js_str_value, str_value, str_length);
+        JSStringRelease(js_str_value);
+#endif
+
+        if(htmlmessagefunc && !exception)
+            htmlmessagefunc(window, name, str_value, user_data);
+
+        g_free(str_value);
+
+        if(!exception)
+          return;
+    }
+    if(htmlmessagefunc)
+        htmlmessagefunc(window, name, NULL, user_data);
+}
+#endif
+
 #ifdef USE_WEBKIT
 #ifdef USE_WEBKIT2
 static void _dw_html_changed_event(WebKitWebView  *web_view, WebKitLoadEvent load_event, gpointer data)
@@ -11898,6 +11963,60 @@
 #endif
 }
 
+/* Free the name when the signal disconnects */
+void _dw_html_message_disconnect(gpointer gdata, GClosure *closure)
+{
+    gpointer *data = (gpointer *)gdata;
+
+    if(data)
+    {
+        gchar *name = (gchar *)data[1];
+
+        if(name)
+            g_free(name);
+        free(data);
+    }
+}
+
+/*
+ * Install a javascript function with name that can call native code.
+ * Parameters:
+ *       handle: Handle to the HTML window.
+ *       name: Javascript function name.
+ * Notes: A DW_SIGNAL_HTML_MESSAGE event will be raised with scriptdata.
+ * Returns:
+ *       DW_ERROR_NONE (0) on success.
+ */
+int API dw_html_javascript_add(HWND handle, const char *name)
+{
+#ifdef USE_WEBKIT2
+   WebKitWebView *web_view= _dw_html_web_view(handle);
+   WebKitUserContentManager *manager;
+
+    if(web_view && (manager = webkit_web_view_get_user_content_manager(web_view)) && name) 
+    {
+        /* Script to inject that will call the handler we are adding */
+        gchar *script = g_strdup_printf("function %s(body) {window.webkit.messageHandlers.%s.postMessage(body);}", 
+                            name, name);
+        gchar *signal = g_strdup_printf("script-message-received::%s", name);
+        WebKitUserScript *userscript = webkit_user_script_new(script, WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES,
+                                                              WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_START, NULL, NULL);
+        gpointer *data = calloc(sizeof(gpointer), 2);
+
+        data[0] = handle;
+        data[1] = g_strdup(name);
+        g_signal_connect_data(manager, signal, G_CALLBACK(_dw_html_message_event), data, _dw_html_message_disconnect, 0);
+        webkit_user_content_manager_register_script_message_handler(manager, name);
+        webkit_user_content_manager_add_script(manager, userscript);
+
+        g_free(script);
+        g_free(signal);
+        return DW_ERROR_NONE;
+    }
+#endif
+    return DW_ERROR_UNKNOWN;
+}
+
 #if defined(USE_WEBKIT) && !defined(USE_WEBKIT2)
 static void _dw_html_print_cb(GtkWidget *widget, gpointer *data)
 {
@@ -12390,7 +12509,8 @@
  */
 void dw_signal_connect_data(HWND window, const char *signame, void *sigfunc, void *discfunc, void *data)
 {
-   void *thisfunc  = _dw_findsigfunc(signame);
+   DWSignalList signal = _dw_findsignal(signame);
+   void *thisfunc  = signal.func;
    char *thisname = (char *)signame;
    HWND thiswindow = window;
    int sigid, _dw_locked_by_me = FALSE;
@@ -12433,8 +12553,9 @@
    }
    else if (GTK_IS_MENU_ITEM(thiswindow) && strcmp(signame, DW_SIGNAL_CLICKED) == 0)
    {
+      DWSignalList signal = _dw_findsignal(signame);
       thisname = "activate";
-      thisfunc = _dw_findsigfunc(thisname);
+      thisfunc = signal.func;
    }
    else if (GTK_IS_TREE_VIEW(thiswindow)  && strcmp(signame, DW_SIGNAL_ITEM_CONTEXT) == 0)
    {
@@ -12477,6 +12598,7 @@
    }
    else if (GTK_IS_TREE_VIEW(thiswindow) && strcmp(signame, DW_SIGNAL_ITEM_ENTER) == 0)
    {
+      DWSignalList signal = _dw_findsignal(DW_SIGNAL_ITEM_ENTER);
       sigid = _dw_set_signal_handler(thiswindow, window, sigfunc, data, _dw_container_enter_event, discfunc);
       params[0] = GINT_TO_POINTER(sigid);
       params[2] = (void *)thiswindow;
@@ -12486,7 +12608,7 @@
       params = calloc(sizeof(void *), _DW_INTERNAL_CALLBACK_PARAMS);
 
       thisname = "button_press_event";
-      thisfunc = _dw_findsigfunc(DW_SIGNAL_ITEM_ENTER);
+      thisfunc = signal.func;
    }
    else if (GTK_IS_TREE_VIEW(thiswindow) && strcmp(signame, DW_SIGNAL_COLUMN_CLICK) == 0)
    {
@@ -12545,6 +12667,18 @@
       DW_MUTEX_UNLOCK;
       return;
    }
+#ifdef USE_WEBKIT2
+   else if (WEBKIT_IS_WEB_VIEW(thiswindow) && strcmp(signame, DW_SIGNAL_HTML_MESSAGE) == 0)
+   {
+      /* We don't actually need a signal handler here... just need to assign the handler ID
+       * Since the handler is created in dw_html_javasript_add()
+       */
+      sigid = _dw_set_signal_handler(thiswindow, window, sigfunc, data, _dw_html_message_event, discfunc);
+      g_object_set_data(G_OBJECT(thiswindow), "_dw_html_message_id", GINT_TO_POINTER(sigid+1));
+      DW_MUTEX_UNLOCK;
+      return;
+   }
+#endif
 #endif
 #if 0
    else if (strcmp(signame, DW_SIGNAL_LOSE_FOCUS) == 0)
@@ -12585,15 +12719,15 @@
  */
 void dw_signal_disconnect_by_name(HWND window, const char *signame)
 {
+   DWSignalList signal = _dw_findsignal(signame);
+   void *thisfunc = signal.func;
    int z, count;
-   void *thisfunc;
    int _dw_locked_by_me = FALSE;
    void **params = alloca(sizeof(void *) * 3);
 
    DW_MUTEX_LOCK;
    params[2] = _dw_find_signal_window(window, signame);
    count = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(params[2]), "_dw_sigcounter"));
-   thisfunc = _dw_findsigfunc(signame);
 
    for(z=0;z<count;z++)
    {