changeset 2981:0058ab32e1bd

Win: Support for dw_html_javascript_add() and DW_SIGNAL_HTML_MESSAGE. This only works with Microsoft Edge WebView2 currently.
author bsmith@81767d24-ef19-dc11-ae90-00e081727c95
date Sun, 26 Mar 2023 22:10:10 +0000
parents 7e273fec75ae
children e6fb2558e29e
files win/dw.c win/dw.def win/edge.cpp
diffstat 3 files changed, 149 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- a/win/dw.c	Sat Mar 25 20:10:05 2023 +0000
+++ b/win/dw.c	Sun Mar 26 22:10:10 2023 +0000
@@ -392,9 +392,7 @@
 static int in_checkbox_handler = 0;
 
 /* List of signals and their equivalent Win32 message */
-#define SIGNALMAX 19
-
-DWSignalList DWSignalTranslate[SIGNALMAX] = {
+DWSignalList DWSignalTranslate[] = {
    { WM_SIZE,         DW_SIGNAL_CONFIGURE },
    { WM_CHAR,         DW_SIGNAL_KEY_PRESS },
    { WM_LBUTTONDOWN,  DW_SIGNAL_BUTTON_PRESS },
@@ -413,7 +411,9 @@
    { LVN_COLUMNCLICK, DW_SIGNAL_COLUMN_CLICK },
    { TVN_ITEMEXPANDED,DW_SIGNAL_TREE_EXPAND },
    { WM_USER+100,     DW_SIGNAL_HTML_RESULT },
-   { WM_USER+101,     DW_SIGNAL_HTML_CHANGED }
+   { WM_USER+101,     DW_SIGNAL_HTML_CHANGED },
+   { WM_USER+103,     DW_SIGNAL_HTML_MESSAGE },
+   { 0,               "" }
 };
 
 #ifdef BUILD_DLL
@@ -1133,12 +1133,13 @@
 /* Finds the message number for a given signal name */
 ULONG _dw_findsigmessage(const char *signame)
 {
-   int z;
-
-   for(z=0;z<SIGNALMAX;z++)
+   int z = 0;
+
+   while(DWSignalTranslate[z].message)
    {
       if(_stricmp(signame, DWSignalTranslate[z].name) == 0)
          return DWSignalTranslate[z].message;
+      z++;
    }
    return 0L;
 }
@@ -2747,6 +2748,16 @@
                      }
                   }
                   break;
+               case WM_USER+103:
+                  {
+                     if(hWnd == tmp->window)
+                     {
+                        int (DWSIGNAL *htmlmessagefunc)(HWND, char *, char *, void *) = tmp->signalfunction;
+
+                        return htmlmessagefunc(tmp->window, (char *)mp1, (char *)mp2, tmp->data);
+                     }
+                  }
+                  break;
             }
          }
          if(tmp)
@@ -6301,6 +6312,7 @@
 int _dw_edge_raw(HWND hwnd, const char *string);
 int _dw_edge_url(HWND hwnd, const char *url);
 int _dw_edge_javascript_run(HWND hwnd, const char *script, void *scriptdata);
+int _dw_edge_javascript_add(HWND hwnd, const char *name);
 #endif
 #endif
 
@@ -6390,6 +6402,26 @@
 }
 
 /*
+ * 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 BUILD_HTML
+#ifdef BUILD_EDGE
+   if (_DW_EDGE_DETECTED)
+      return _dw_edge_javascript_add(handle, name);
+#endif
+#endif
+   return DW_ERROR_UNKNOWN;
+}
+
+/*
  * Create a bitmap object to be packed.
  * Parameters:
  *       id: An ID to be used with dw_window_from_id or 0L.
@@ -13937,6 +13969,10 @@
         case DW_FEATURE_TREE:
         case DW_FEATURE_WINDOW_PLACEMENT:
             return DW_FEATURE_ENABLED;
+#if defined(BUILD_HTML) && defined(BUILD_EDGE)
+        case DW_FEATURE_HTML_MESSAGE:
+            return _DW_EDGE_DETECTED ? DW_FEATURE_ENABLED : DW_FEATURE_UNSUPPORTED;
+#endif
 #ifdef BUILD_TOAST
         case DW_FEATURE_NOTIFICATION:
         {
@@ -14023,6 +14059,10 @@
         case DW_FEATURE_TREE:
         case DW_FEATURE_WINDOW_PLACEMENT:
             return DW_ERROR_GENERAL;
+#if defined(BUILD_HTML) && defined(BUILD_EDGE)
+        case DW_FEATURE_HTML_MESSAGE:
+            return _DW_EDGE_DETECTED ? DW_ERROR_GENERAL : DW_FEATURE_UNSUPPORTED;
+#endif
 #ifdef BUILD_TOAST
         case DW_FEATURE_NOTIFICATION:
         {
--- a/win/dw.def	Sat Mar 25 20:10:05 2023 +0000
+++ b/win/dw.def	Sun Mar 26 22:10:10 2023 +0000
@@ -311,6 +311,7 @@
   dw_html_raw                            @472
   dw_html_url                            @473
   dw_html_javascript_run                 @474
+  dw_html_javascript_add                 @475
 
   dw_calendar_new                        @480
   dw_calendar_set_date                   @481
--- a/win/edge.cpp	Sat Mar 25 20:10:05 2023 +0000
+++ b/win/edge.cpp	Sun Mar 26 22:10:10 2023 +0000
@@ -9,12 +9,14 @@
 #include "dw.h"
 #include "WebView2.h"
 #include <wrl.h>
+#include <string>
 
 using namespace Microsoft::WRL;
 
 #define _DW_HTML_DATA_NAME "_dw_edge"
 #define _DW_HTML_DATA_LOCATION "_dw_edge_location"
 #define _DW_HTML_DATA_RAW "_dw_edge_raw"
+#define _DW_HTML_DATA_ADD "_dw_edge_add"
 
 extern "C" {
 
@@ -44,6 +46,7 @@
 	int Raw(const char* string);
 	int URL(const char* url);
 	int JavascriptRun(const char* script, void* scriptdata);
+	int JavascriptAdd(const char* name);
 	VOID DoSize(VOID);
 	VOID Setup(HWND hwnd, ICoreWebView2Controller* webview);
 	VOID Close(VOID);
@@ -166,6 +169,32 @@
 
 									return S_OK;
 								}).Get(), &token);
+								
+						// Register a handler for the WebMessageReceived event.
+						webview->add_WebMessageReceived(
+							Callback<ICoreWebView2WebMessageReceivedEventHandler>(
+								[hWnd](ICoreWebView2* sender,
+									ICoreWebView2WebMessageReceivedEventArgs* args) -> HRESULT
+								{
+									LPWSTR message, name = NULL, body = NULL;
+									args->TryGetWebMessageAsString(&message);
+
+									// Locate the DWindows|<function name>| signature and body
+									if(message && !wcsncmp(message, L"DWindows|", 9)) {
+										name = message + 9;
+										if(*name) {
+											body = wcschr(name, L'|');
+											if(body) {
+												*body = 0;
+												body++;
+											}
+										}
+									}
+									if(name && body)
+										_dw_wndproc(hWnd, WM_USER + 103, (WPARAM)WideToUTF8(name), (LPARAM)WideToUTF8(body));
+
+									return S_OK;
+								}).Get(), &token);
 					}
 
 					// Resize WebView to fit the bounds of the parent window
@@ -187,6 +216,21 @@
 						dw_window_set_data(hWnd, _DW_HTML_DATA_RAW, NULL);
 						free((void*)raw);
 					}
+					char *adds = (char *)dw_window_get_data(hWnd, _DW_HTML_DATA_ADD);
+					if(adds)
+					{
+						char *start = adds, *separator;
+
+						while((separator = strchr(start, '|')))
+						{
+							*separator = 0;
+							WebView->JavascriptAdd(start);
+							start = separator + 1;
+						}
+						WebView->JavascriptAdd(start);
+						dw_window_set_data(hWnd, _DW_HTML_DATA_ADD, NULL);
+						free((void *)adds);
+					}
 					return S_OK;
 				}).Get());
 			// Success
@@ -366,6 +410,21 @@
 	return DW_ERROR_NONE;
 }
 
+int EdgeWebView::JavascriptAdd(const char* name)
+{
+	if (WebView) {
+		std::wstring wname = std::wstring(UTF8toWide(name));
+		std::wstring script = L"function " + wname + L"(body) {window.chrome.webview.postMessage('DWindows|" + wname + L"|' + body);}";
+		WebView->AddScriptToExecuteOnDocumentCreated(script.c_str(),
+			Callback<ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler>(
+					[this](HRESULT error, PCWSTR id) -> HRESULT {
+						return S_OK;
+					}).Get());
+		return DW_ERROR_NONE;
+	}
+	return DW_ERROR_UNKNOWN;
+}
+
 VOID EdgeWebView::Setup(HWND hwnd, ICoreWebView2Controller* host)
 {
 	hWnd = hwnd;
@@ -506,8 +565,12 @@
 		EdgeWebView* webview = (EdgeWebView*)dw_window_get_data(hwnd, _DW_HTML_DATA_NAME);
 		if (webview)
 			return webview->Raw(string);
-		else
+		else {
+			char *oldstring = (char *)dw_window_get_data(hwnd, _DW_HTML_DATA_RAW);
 			dw_window_set_data(hwnd, _DW_HTML_DATA_RAW, _strdup(string));
+			if(oldstring)
+				free((void *)oldstring);
+		}
 		return DW_ERROR_NONE;
 	}
 
@@ -525,8 +588,12 @@
 		EdgeWebView* webview = (EdgeWebView*)dw_window_get_data(hwnd, _DW_HTML_DATA_NAME);
 		if (webview)
 			return webview->URL(url);
-		else
+		else {
+			char *oldurl = (char *)dw_window_get_data(hwnd, _DW_HTML_DATA_LOCATION);
 			dw_window_set_data(hwnd, _DW_HTML_DATA_LOCATION, _strdup(url));
+			if(oldurl)
+				free((void *)oldurl);
+		}
 		return DW_ERROR_NONE;
 	}
 
@@ -548,6 +615,38 @@
 		return DW_ERROR_UNKNOWN;
 	}
 
+	/******************************* dw_edge_javascript_add() ****************************
+	 * Adds a javascript function in the specified browser context.
+	 *
+	 * hwnd			=	Handle to the window hosting the browser object.
+	 * name			=	Pointer to nul-terminated javascript function name string.
+	 *
+	 * RETURNS: 0 if success, or non-zero if an error.
+	 */
+
+	int _dw_edge_javascript_add(HWND hwnd, const char* name)
+	{
+		if(name) {
+			EdgeWebView* webview = (EdgeWebView*)dw_window_get_data(hwnd, _DW_HTML_DATA_NAME);
+			if (webview)
+				return webview->JavascriptAdd(name);
+			else {
+				char *oldadd = (char *)dw_window_get_data(hwnd, _DW_HTML_DATA_ADD);
+				char *newadd = (char *)calloc(strlen(name) + (oldadd ? strlen(oldadd) : 0) + 2, 1);
+				if(oldadd) {
+					strcpy(newadd, oldadd);
+					strcat(newadd, "|");
+				}
+				strcat(newadd, name);
+				dw_window_set_data(hwnd, _DW_HTML_DATA_ADD, newadd);
+				if(oldadd)
+					free((void *)oldadd);
+				return DW_ERROR_NONE;
+			}
+		}
+		return DW_ERROR_UNKNOWN;
+	}
+
 	/************************** edgeWindowProc() *************************
 	 * Our message handler for our window to host the browser.
 	 */