# HG changeset patch # User bsmith@81767d24-ef19-dc11-ae90-00e081727c95 # Date 1572505295 0 # Node ID 4e808c4cadfb96844f456d7ac961a7c04d201219 # Parent a3de27b07a8dbf085e50d0438552fed0892e4fe5 Win: Add initial support for Microsoft Edge (Chromium) embedding. Only works with Visual Studio currently due to the SDK being a nuget package. diff -r a3de27b07a8d -r 4e808c4cadfb win/dw.c --- 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; diff -r a3de27b07a8d -r 4e808c4cadfb win/edge.cpp --- /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... + * + *

This is a picture.

+ * + * hwnd = Handle to the window hosting the browser object. + * string = Pointer to nul-terminated string containing the HTML BODY. + * (NOTE: No 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( + [hWnd](HRESULT result, IWebView2Environment* env) -> HRESULT { + + // Create a WebView, whose parent is the main window hWnd + env->CreateWebView(hWnd, Callback( + [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)); +} +