changeset 2974:fffb4904c90b

Mac: Initial support for dw_html_javascript_add() and DW_SIGNAL_HTML_MESSAGE for callbacks into native code. Code for iOS next... Then GTK3/4, Windows and Android in that order. Also implemented simple rawhtml tests in the test programs. C++: Added HTML class support for the message callbacks.
author bsmith@81767d24-ef19-dc11-ae90-00e081727c95
date Fri, 24 Mar 2023 11:39:24 +0000
parents ac880987a2c4
children ae4d6856b983
files dw.h dw.hpp dwtest.c dwtestoo.cpp mac/dw.m
diffstat 5 files changed, 136 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
--- a/dw.h	Tue Mar 21 00:41:25 2023 +0000
+++ b/dw.h	Fri Mar 24 11:39:24 2023 +0000
@@ -108,6 +108,7 @@
 #define DW_SIGNAL_TREE_EXPAND    "tree-expand"
 #define DW_SIGNAL_HTML_CHANGED   "html-changed"
 #define DW_SIGNAL_HTML_RESULT    "html-result"
+#define DW_SIGNAL_HTML_MESSAGE   "html-message"
 
 /* status of menu items */
 #define DW_MIS_ENABLED           1
@@ -1659,6 +1660,7 @@
     _DW_EVENT_COLUMN_CLICK,             /* Internal message for (container) column clicked */
     _DW_EVENT_HTML_RESULT,              /* Internal message for HTML javascript result */
     _DW_EVENT_HTML_CHANGED,             /* Internal message for HTML status changed */
+    _DW_EVENT_HTML_MESSAGE,             /* Internal message for HTML javascript message */
     _DW_EVENT_MAX
 } _DW_EVENTS;
 #endif
@@ -2235,6 +2237,7 @@
 int API dw_html_raw(HWND hwnd, const char *string);
 int API dw_html_url(HWND hwnd, const char *url);
 int API dw_html_javascript_run(HWND hwnd, const char *script, void *scriptdata);
+int API dw_html_javascript_add(HWND hwnd, const char *name);
 HWND API dw_html_new(unsigned long id);
 char * API dw_clipboard_get_text(void);
 void API dw_clipboard_set_text(const char *str, int len);
--- a/dw.hpp	Tue Mar 21 00:41:25 2023 +0000
+++ b/dw.hpp	Fri Mar 24 11:39:24 2023 +0000
@@ -1098,24 +1098,30 @@
 class HTML : public Widget
 {
 private:
-    bool ChangedConnected, ResultConnected;
+    bool ChangedConnected, ResultConnected, MessageConnected;
 #ifdef DW_LAMBDA
     std::function<int(int, std::string)> _ConnectChanged;
     std::function<int(int, std::string, void *)> _ConnectResult;
+    std::function<int(std::string, std::string)> _ConnectMessage;
 #endif
     int (*_ConnectCChangedOld)(HTML *, int status, char *url);
     int (*_ConnectCResultOld)(HTML *, int status, char *result, void *scriptdata);
+    int (*_ConnectCMessageOld)(HTML *, char *name, char *message);
     int (*_ConnectChangedOld)(HTML *, int status, std::string url);
     int (*_ConnectResultOld)(HTML *, int status, std::string result, void *scriptdata);
+    int (*_ConnectMessageOld)(HTML *, std::string, std::string message);
     void Setup() {
 #ifdef DW_LAMBDA
         _ConnectChanged = 0;
         _ConnectResult = 0;
+        _ConnectMessage = 0;
 #endif
         _ConnectCChangedOld = 0;
         _ConnectCResultOld = 0;
+        _ConnectCMessageOld = 0;
         _ConnectChangedOld = 0;
         _ConnectResultOld = 0;
+        _ConnectMessageOld = 0;
         if(IsOverridden(HTML::OnChanged, this)) {
             dw_signal_connect(hwnd, DW_SIGNAL_HTML_CHANGED, DW_SIGNAL_FUNC(_OnChanged), this);
             ChangedConnected = true;
@@ -1127,7 +1133,13 @@
             ResultConnected = true;
         } else {
             ResultConnected = false;
-    }
+        }
+        if(IsOverridden(HTML::OnMessage, this)) {
+            dw_signal_connect(hwnd, DW_SIGNAL_HTML_MESSAGE, DW_SIGNAL_FUNC(_OnMessage), this);
+            MessageConnected = true;
+        } else {
+            MessageConnected = false;
+        }
     }
     static int _OnChanged(HWND window, int status, char *url, void *data) {
         HTML *classptr = reinterpret_cast<HTML *>(data);
@@ -1153,6 +1165,19 @@
         else if(classptr->_ConnectResultOld)
             return classptr->_ConnectResultOld(classptr, status, utf8string, scriptdata);
         return classptr->OnResult(status, utf8string, scriptdata); }
+        static int _OnMessage(HWND window, char *name, char *message, void *data) {
+            HTML *classptr = reinterpret_cast<HTML *>(data);
+            std::string utf8name = name ? std::string(name) : std::string();
+            std::string utf8message = message ? std::string(message) : std::string();
+    #ifdef DW_LAMBDA
+            if(classptr->_ConnectMessage)
+                return classptr->_ConnectMessage(utf8name, utf8message);
+    #endif
+            if(classptr->_ConnectCMessageOld)
+                return classptr->_ConnectCMessageOld(classptr, name, message);
+            else if(classptr->_ConnectMessageOld)
+                return classptr->_ConnectMessageOld(classptr, utf8name, utf8message);
+            return classptr->OnMessage(utf8name, utf8message); }
 public:
     // Constructors
     HTML(unsigned long id) { SetHWND(dw_html_new(id)); Setup(); }
@@ -1162,10 +1187,12 @@
     void Action(int action) { dw_html_action(hwnd, action); }
     int JavascriptRun(const char *script, void *scriptdata) { return dw_html_javascript_run(hwnd, script, scriptdata); }
     int JavascriptRun(const char *script) { return dw_html_javascript_run(hwnd, script, NULL); }
+    int JavascriptAdd(const char *name) { return dw_html_javascript_add(hwnd, name); }
     int Raw(const char *buffer) { return dw_html_raw(hwnd, buffer); }
     int URL(const char *url) { return dw_html_url(hwnd, url); }
     int JavascriptRun(std::string script, void *scriptdata) { return dw_html_javascript_run(hwnd, script.c_str(), scriptdata); }
     int JavascriptRun(std::string script) { return dw_html_javascript_run(hwnd, script.c_str(), NULL); }
+    int JavascriptAdd(std::string name) { return dw_html_javascript_add(hwnd, name.c_str()); }
     int Raw(std::string buffer) { return dw_html_raw(hwnd, buffer.c_str()); }
     int URL(std::string url) { return dw_html_url(hwnd, url.c_str()); }
 #ifdef DW_LAMBDA
@@ -1220,6 +1247,32 @@
             ResultConnected = true;
         }
     }    
+#ifdef DW_LAMBDA
+    void ConnectMessage(std::function<int(std::string, std::string)> userfunc)
+    {
+        _ConnectMessage = userfunc;
+        if(!MessageConnected) {
+            dw_signal_connect(hwnd, DW_SIGNAL_HTML_MESSAGE, DW_SIGNAL_FUNC(_OnMessage), this);
+            MessageConnected = true;
+        }
+    }    
+#endif
+    void ConnectMessage(int (*userfunc)(HTML *, char *, char *))
+    {
+        _ConnectCMessageOld = userfunc;
+        if(!MessageConnected) {
+            dw_signal_connect(hwnd, DW_SIGNAL_HTML_MESSAGE, DW_SIGNAL_FUNC(_OnMessage), this);
+            MessageConnected = true;
+        }
+    }    
+    void ConnectMessage(int (*userfunc)(HTML *, std::string, std::string))
+    {
+        _ConnectMessageOld = userfunc;
+        if(!MessageConnected) {
+            dw_signal_connect(hwnd, DW_SIGNAL_HTML_MESSAGE, DW_SIGNAL_FUNC(_OnMessage), this);
+            MessageConnected = true;
+        }
+    }    
 protected:
     // Our signal handler functions to be overriden...
     // If they are not overridden and an event is generated, remove the unused handler
@@ -1233,6 +1286,11 @@
         ResultConnected = false;
         return FALSE;
     };
+    virtual int OnMessage(std::string name, std::string message) {
+        dw_signal_disconnect_by_name(hwnd, DW_SIGNAL_HTML_MESSAGE);
+        MessageConnected = false;
+        return FALSE;
+    };
 };
 
 // Base class for several widgets that allow text entry
--- a/dwtest.c	Tue Mar 21 00:41:25 2023 +0000
+++ b/dwtest.c	Fri Mar 24 11:39:24 2023 +0000
@@ -2080,6 +2080,14 @@
     return FALSE;
 }
 
+/* Handle web javascript message */
+int DWSIGNAL web_html_message(HWND html, char *name, char *message, void *data)
+{
+    dw_messagebox("Javascript Message", DW_MB_OK | DW_MB_INFORMATION, "Name: %s Message: %s",
+                  name, message);
+    return TRUE;
+}
+
 void html_add(void)
 {
     rawhtml = dw_html_new(1001);
@@ -2095,7 +2103,9 @@
         dw_listbox_append(javascript, "window.navigator.userAgent;");
 
         dw_box_pack_start(notebookbox7, rawhtml, 0, 100, TRUE, FALSE, 0);
-        dw_html_raw(rawhtml, "<html><body><center><h1>dwtest</h1></center></body></html>");
+        dw_html_javascript_add(rawhtml, "test");
+        dw_signal_connect(rawhtml, DW_SIGNAL_HTML_MESSAGE, DW_SIGNAL_FUNC(web_html_message), DW_POINTER(javascript));
+        dw_html_raw(rawhtml, "<html><body><center><h1><a href=\"javascript:test('This is the message');\">dwtest</a></h1></center></body></html>");
         html = dw_html_new(1002);
 
         dw_box_pack_start(notebookbox7, hbox, 0, 0, TRUE, FALSE, 0);
--- a/dwtestoo.cpp	Tue Mar 21 00:41:25 2023 +0000
+++ b/dwtestoo.cpp	Fri Mar 24 11:39:24 2023 +0000
@@ -1616,7 +1616,14 @@
             javascript->Append("window.navigator.userAgent;");
 
             notebookbox->PackStart(rawhtml, 0, 100, TRUE, FALSE, 0);
-            rawhtml->Raw("<html><body><center><h1>dwtest</h1></center></body></html>");
+            rawhtml->JavascriptAdd("test");
+            rawhtml->ConnectMessage([this](std::string name, std::string message) -> int
+            {
+                this->app->MessageBox("Javascript Message", DW_MB_OK | DW_MB_INFORMATION,
+                              "Name: " + name + " Message: " + message);
+                return TRUE;
+            });
+            rawhtml->Raw("<html><body><center><h1><a href=\"javascript:test('This is the message');\">dwtest</a></h1></center></body></html>");
             DW::HTML *html = new DW::HTML();
 
             notebookbox->PackStart(hbox, 0, 0, TRUE, FALSE, 0);
@@ -1682,7 +1689,7 @@
                 return FALSE;
             });
 
-            html->ConnectResult([this](int status, std::string result, void *script_data)
+            html->ConnectResult([this](int status, std::string result, void *script_data) -> int
             {
                 this->app->MessageBox("Javascript Result", DW_MB_OK | (status ? DW_MB_ERROR : DW_MB_INFORMATION),
                               result.size() ? result : "Javascript result is not a string value");
--- a/mac/dw.m	Tue Mar 21 00:41:25 2023 +0000
+++ b/mac/dw.m	Fri Mar 24 11:39:24 2023 +0000
@@ -592,9 +592,7 @@
 } DWSignalList;
 
 /* List of signals */
-#define SIGNALMAX 19
-
-static DWSignalList DWSignalTranslate[SIGNALMAX] = {
+static DWSignalList DWSignalTranslate[] = {
     { _DW_EVENT_CONFIGURE,       DW_SIGNAL_CONFIGURE },
     { _DW_EVENT_KEY_PRESS,       DW_SIGNAL_KEY_PRESS },
     { _DW_EVENT_BUTTON_PRESS,    DW_SIGNAL_BUTTON_PRESS },
@@ -613,7 +611,8 @@
     { _DW_EVENT_TREE_EXPAND,     DW_SIGNAL_TREE_EXPAND },
     { _DW_EVENT_COLUMN_CLICK,    DW_SIGNAL_COLUMN_CLICK },
     { _DW_EVENT_HTML_RESULT,     DW_SIGNAL_HTML_RESULT },
-    { _DW_EVENT_HTML_CHANGED,    DW_SIGNAL_HTML_CHANGED }
+    { _DW_EVENT_HTML_CHANGED,    DW_SIGNAL_HTML_CHANGED },
+    { _DW_EVENT_HTML_MESSAGE,    DW_SIGNAL_HTML_MESSAGE }
 };
 
 int _dw_event_handler1(id object, NSEvent *event, int message)
@@ -877,7 +876,8 @@
                 void **params = (void **)event;
                 NSString *result = params[0];
 
-                return htmlresultfunc(handler->window, [result length] ? DW_ERROR_NONE : DW_ERROR_UNKNOWN, [result length] ? (char *)[result UTF8String] : NULL, params[1], handler->data);
+                return htmlresultfunc(handler->window, [result length] ? DW_ERROR_NONE : DW_ERROR_UNKNOWN, [result length] ? 
+                                      (char *)[result UTF8String] : NULL, params[1], handler->data);
             }
             /* HTML changed event */
             case _DW_EVENT_HTML_CHANGED:
@@ -888,6 +888,15 @@
 
                 return htmlchangedfunc(handler->window, DW_POINTER_TO_INT(params[0]), (char *)[uri UTF8String], handler->data);
             }
+            /* HTML message event */
+            case _DW_EVENT_HTML_MESSAGE:
+            {
+                int (* API htmlmessagefunc)(HWND, char *, char *, void *) = handler->signalfunction;
+                WKScriptMessage *message = (WKScriptMessage *)event;
+
+                return htmlmessagefunc(handler->window, (char *)[[message name] UTF8String], [[message body] isKindOfClass:[NSString class]] ?
+                                       (char *)[[message body] UTF8String] : NULL, handler->data);
+            }
         }
     }
     return -1;
@@ -1447,7 +1456,7 @@
  * WKWebKit on intel 64 and the old WebKit on intel 32 bit and earlier.
  */
 #if WK_API_ENABLED
-@interface DWWebView : WKWebView <WKNavigationDelegate>
+@interface DWWebView : WKWebView <WKNavigationDelegate, WKScriptMessageHandler>
 {
     void *userdata;
 }
@@ -1457,6 +1466,7 @@
 -(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation;
 -(void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation;
 -(void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation;
+-(void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;
 @end
 
 @implementation DWWebView : WKWebView
@@ -1482,6 +1492,10 @@
     void *params[2] = { DW_INT_TO_POINTER(DW_HTML_CHANGE_REDIRECT), [[self URL] absoluteString] };
     _dw_event_handler(self, (NSEvent *)params, _DW_EVENT_HTML_CHANGED);
 }
+-(void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
+{
+    _dw_event_handler(self, (NSEvent *)message, _DW_EVENT_HTML_MESSAGE);
+}
 -(void)dealloc { UserData *root = userdata; _dw_remove_userdata(&root, NULL, TRUE); dw_signal_disconnect_by_window(self); [super dealloc]; }
 @end
 #else
@@ -3695,7 +3709,7 @@
 {
     int z;
 
-    for(z=0;z<SIGNALMAX;z++)
+    for(z=0;z<(_DW_EVENT_MAX-1);z++)
     {
         if(strcasecmp(signame, DWSignalTranslate[z].name) == 0)
             return DWSignalTranslate[z].message;
@@ -9468,6 +9482,38 @@
 }
 
 /*
+ * 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)
+{
+#if WK_API_ENABLED
+    DWWebView *html = handle;
+    WKUserContentController *controller = [[html configuration] userContentController];
+    
+    if(controller && name) 
+    {
+        DW_LOCAL_POOL_IN;
+        /* Script to inject that will call the handler we are adding */
+        NSString *script = [NSString stringWithFormat:@"function %s(body) {window.webkit.messageHandlers.%s.postMessage(body);}", 
+                            name, name];
+        [controller addScriptMessageHandler:handle name:[NSString stringWithUTF8String:name]];
+        [controller addUserScript:[[WKUserScript alloc] initWithSource:script 
+                injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO]];
+        DW_LOCAL_POOL_OUT;
+        return DW_ERROR_NONE;
+    }
+#endif
+    return DW_ERROR_UNKNOWN;
+}
+
+
+/*
  * Create a new HTML window (widget) to be packed.
  * Not available under OS/2, eCS
  * Parameters: