changeset 1999:4e808c4cadfb

Win: Add initial support for Microsoft Edge (Chromium) embedding. Only works with Visual Studio currently due to the SDK being a nuget package.
author bsmith@81767d24-ef19-dc11-ae90-00e081727c95
date Thu, 31 Oct 2019 07:01:35 +0000
parents a3de27b07a8d
children 77e43d71eaa7
files win/dw.c win/edge.cpp
diffstat 2 files changed, 280 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/win/dw.c	Fri Oct 25 22:37:52 2019 +0000
+++ b/win/dw.c	Thu Oct 31 07:01:35 2019 +0000
@@ -310,6 +310,10 @@
 
 #if (defined(BUILD_DLL) || defined(BUILD_HTML))
 LRESULT CALLBACK _browserWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+#ifdef BUILD_EDGE
+BOOL _DW_EDGE_DETECTED = FALSE;
+LRESULT CALLBACK _edgeWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+#endif
 #endif
 LRESULT CALLBACK _colorwndproc(HWND hWnd, UINT msg, WPARAM mp1, LPARAM mp2);
 void _resize_notebook_page(HWND handle, int pageid);
@@ -4283,9 +4287,19 @@
 #if (defined(BUILD_DLL) || defined(BUILD_HTML))
    /* Register HTML renderer class */
    memset(&wc, 0, sizeof(WNDCLASS));
-   wc.lpfnWndProc = (WNDPROC)_browserWindowProc;
    wc.lpszClassName = BrowserClassName;
-   wc.style = CS_HREDRAW|CS_VREDRAW;
+   wc.style = CS_HREDRAW | CS_VREDRAW;
+#ifdef BUILD_EDGE
+   /* Check if Microsoft Edge (Chromium) is installed */
+   if(_DW_EDGE_DETECTED = _dw_edge_detect())
+   {
+      wc.lpfnWndProc = (WNDPROC)_edgeWindowProc;
+   }
+   else
+#endif
+   {
+      wc.lpfnWndProc = (WNDPROC)_browserWindowProc;
+   }
    RegisterClass(&wc);
 #endif
 
@@ -5683,6 +5697,12 @@
 void _dw_html_action(HWND hwnd, int action);
 int _dw_html_raw(HWND hwnd, char *string);
 int _dw_html_url(HWND hwnd, char *url);
+#ifdef BUILD_EDGE
+void _dw_edge_action(HWND hwnd, int action);
+int _dw_edge_raw(HWND hwnd, LPCWSTR string);
+int _dw_edge_url(HWND hwnd, LPCWSTR url);
+BOOL _dw_edge_detect(VOID);
+#endif
 #endif
 
 /*
@@ -5694,6 +5714,11 @@
 void API dw_html_action(HWND handle, int action)
 {
 #if (defined(BUILD_DLL) || defined(BUILD_HTML))
+#ifdef BUILD_EDGE
+   if (_DW_EDGE_DETECTED)
+      _dw_edge_action(handle, action);
+   else
+#endif
    _dw_html_action(handle, action);
 #endif
 }
@@ -5710,6 +5735,10 @@
 int API dw_html_raw(HWND handle, char *string)
 {
 #if (defined(BUILD_DLL) || defined(BUILD_HTML))
+#ifdef BUILD_EDGE
+   if (_DW_EDGE_DETECTED)
+      return _dw_edge_raw(handle, UTF8toWide(string));
+#endif
    return _dw_html_raw(handle, string);
 #else
    return DW_ERROR_GENERAL;
@@ -5728,6 +5757,10 @@
 int API dw_html_url(HWND handle, char *url)
 {
 #if (defined(BUILD_DLL) || defined(BUILD_HTML))
+#if BUILD_EDGE
+   if (_DW_EDGE_DETECTED)
+      return _dw_edge_url(handle, UTF8toWide(url));
+#endif
    return _dw_html_url(handle, url);
 #else
    return DW_ERROR_GENERAL;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/win/edge.cpp	Thu Oct 31 07:01:35 2019 +0000
@@ -0,0 +1,245 @@
+/* edge.cpp
+ *
+ * Allows dw_html_new() to embed a Microsoft Edge (Chromium) browser.
+ *
+ * Requires Windows 10, 8 or 7 with Microsoft Edge (Chromium) installed.
+ * 
+ * Only included when BUILD_EDGE is defined, will fall back to embedded IE.
+ *
+ * Currently only buildable with Visual Studio since it requires the EDGE
+ * SDK which is currently distributed as a nuget package. 
+ */
+#include "dw.h"
+#include "webview2.h"
+
+#define _DW_HTML_DATA_NAME (char *)"_dw_edge"
+
+BOOL _dw_edge_detect(VOID)
+{
+	return TRUE;
+}
+
+/******************************* dw_html_action() **************************
+ * Implements the functionality of a "Back". "Forward", "Home", "Search",
+ * "Refresh", or "Stop" button.
+ *
+ * hwnd =		Handle to the window hosting the browser object.
+ * action =		One of the following:
+ *				0 = Move back to the previously viewed web page.
+ *				1 = Move forward to the previously viewed web page.
+ *				2 = Move to the home page.
+ *				3 = Search.
+ *				4 = Refresh the page.
+ *				5 = Stop the currently loading page.
+ *
+ * NOTE: EmbedBrowserObject() must have been successfully called once with the
+ * specified window, prior to calling this function. You need call
+ * EmbedBrowserObject() once only, and then you can make multiple calls to
+ * this function to display numerous pages in the specified window.
+ */
+
+void _dw_edge_action(HWND hwnd, int action)
+{
+	IWebView2WebView* webview;
+
+	// Retrieve the browser object's pointer we stored in our window's GWL_USERDATA when
+	// we initially attached the browser object to this window.
+
+	webview = *((IWebView2WebView**)dw_window_get_data(hwnd, _DW_HTML_DATA_NAME));
+	// We want to get the base address (ie, a pointer) to the IWebBrowser2 object embedded within the browser
+	// object, so we can call some of the functions in the former's table.
+	if (webview)
+	{
+		// Ok, now the pointer to our IWebBrowser2 object is in 'webBrowser2', and so its VTable is
+		// webBrowser2->lpVtbl.
+
+		// Call the desired function
+		switch (action)
+		{
+			case DW_HTML_GOBACK:
+			{
+				// Call the IWebBrowser2 object's GoBack function.
+				webview->GoBack();
+				break;
+			}
+
+			case DW_HTML_GOFORWARD:
+			{
+				// Call the IWebBrowser2 object's GoForward function.
+				webview->GoForward();
+				break;
+			}
+
+			case DW_HTML_GOHOME:
+			{
+				// Call the IWebBrowser2 object's GoHome function.
+				//webview->GoHome();
+				break;
+			}
+
+			case DW_HTML_SEARCH:
+			{
+				// Call the IWebBrowser2 object's GoSearch function.
+				//webview->GoSearch();
+				break;
+			}
+
+			case DW_HTML_RELOAD:
+			{
+				// Call the IWebBrowser2 object's Refresh function.
+				webview->Reload();
+			}
+
+			case DW_HTML_STOP:
+			{
+				// Call the IWebBrowser2 object's Stop function.
+				//webview->Stop();
+			}
+		}
+	}
+}
+
+/******************************* dw_html_raw() ****************************
+ * Takes a string containing some HTML BODY, and displays it in the specified
+ * window. For example, perhaps you want to display the HTML text of...
+ *
+ * <P>This is a picture.<P><IMG src="mypic.jpg">
+ *
+ * hwnd =		Handle to the window hosting the browser object.
+ * string =		Pointer to nul-terminated string containing the HTML BODY.
+ *				(NOTE: No <BODY></BODY> tags are required in the string).
+ *
+ * RETURNS: 0 if success, or non-zero if an error.
+ *
+ * NOTE: EmbedBrowserObject() must have been successfully called once with the
+ * specified window, prior to calling this function. You need call
+ * EmbedBrowserObject() once only, and then you can make multiple calls to
+ * this function to display numerous pages in the specified window.
+ */
+
+int _dw_edge_raw(HWND hwnd, LPCWSTR string)
+{
+	IWebView2WebView* webview;
+
+	// Retrieve the browser object's pointer we stored in our window's GWL_USERDATA when
+	// we initially attached the browser object to this window.
+	webview = *((IWebView2WebView**)dw_window_get_data(hwnd, _DW_HTML_DATA_NAME));
+
+	if (webview)
+	{
+		return DW_ERROR_NONE;
+	}
+	return DW_ERROR_UNKNOWN;
+}
+
+/******************************* dw_html_url() ****************************
+ * Displays a URL, or HTML file on disk.
+ *
+ * hwnd =		Handle to the window hosting the browser object.
+ * webPageName =	Pointer to nul-terminated name of the URL/file.
+ *
+ * RETURNS: 0 if success, or non-zero if an error.
+ *
+ * NOTE: EmbedBrowserObject() must have been successfully called once with the
+ * specified window, prior to calling this function. You need call
+ * EmbedBrowserObject() once only, and then you can make multiple calls to
+ * this function to display numerous pages in the specified window.
+ */
+
+int _dw_edge_url(HWND hwnd, LPCWSTR url)
+{
+	IWebView2WebView * webview;
+
+	// Retrieve the browser object's pointer we stored in our window's GWL_USERDATA when
+	// we initially attached the browser object to this window.
+	webview = *((IWebView2WebView**)dw_window_get_data(hwnd, _DW_HTML_DATA_NAME));
+
+	if (webview)
+	{
+		webview->Navigate(url);
+		return DW_ERROR_NONE;
+	}
+	return DW_ERROR_UNKNOWN;
+}
+
+/************************** browserWindowProc() *************************
+ * Our message handler for our window to host the browser.
+ */
+
+LRESULT CALLBACK _edgeWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+	switch (uMsg)
+	{
+		case WM_SIZE:
+		{
+			// Resize the browser object to fit the window
+			RECT bounds;
+			IWebView2WebView* webview;
+
+			// Retrieve the browser object's pointer we stored in our window's GWL_USERDATA when
+			// we initially attached the browser object to this window.
+			webview = *((IWebView2WebView**)dw_window_get_data(hWnd, _DW_HTML_DATA_NAME));
+			GetClientRect(hWnd, &bounds);
+			// Resize WebView to fit the bounds of the parent window
+			if(webview)
+				webview->put_Bounds(bounds);
+			return(0);
+		}
+
+		case WM_CREATE:
+		{
+			// Step 3 - Create a single WebView within the parent window
+			// Locate the browser and set up the environment for WebView
+			CreateWebView2EnvironmentWithDetails(nullptr, nullptr, nullptr,
+				Callback<IWebView2CreateWebView2EnvironmentCompletedHandler>(
+					[hWnd](HRESULT result, IWebView2Environment* env) -> HRESULT {
+
+						// Create a WebView, whose parent is the main window hWnd
+						env->CreateWebView(hWnd, Callback<IWebView2CreateWebViewCompletedHandler>(
+							[hWnd](HRESULT result, IWebView2WebView* webview) -> HRESULT {
+								if (webview != nullptr) {
+									dw_window_set_data(hWnd, _DW_HTML_DATA_NAME, DW_POINTER(webview));
+								}
+
+								// Add a few settings for the webview
+								// this is a redundant demo step as they are the default settings values
+								IWebView2Settings* Settings;
+								webview->get_Settings(&Settings);
+								Settings->put_IsScriptEnabled(TRUE);
+								Settings->put_AreDefaultScriptDialogsEnabled(TRUE);
+								Settings->put_IsWebMessageEnabled(TRUE);
+
+								// Resize WebView to fit the bounds of the parent window
+								RECT bounds;
+								GetClientRect(hWnd, &bounds);
+								webview->put_Bounds(bounds);
+								return S_OK;
+							}).Get());
+						return S_OK;
+					}).Get());
+
+			// Success
+			return(0);
+		}
+
+		case WM_DESTROY:
+		{
+			// Detach the browser object from this window, and free resources.
+			IWebView2WebView* webview;
+
+			// Retrieve the browser object's pointer we stored in our window's GWL_USERDATA when
+			// we initially attached the browser object to this window.
+			webview = *((IWebView2WebView**)dw_window_get_data(hWnd, _DW_HTML_DATA_NAME));
+			if (webview)
+			{
+				dw_window_set_data(hWnd, _DW_HTML_DATA_NAME, NULL);
+				webview->Close();
+
+			}
+			return(TRUE);
+		}
+	}
+
+	return(DefWindowProc(hWnd, uMsg, wParam, lParam));
+}
+