changeset 2042:0d8b898b03e2

Win: Rewrite edge.cpp using C++ classes, EdgeBrowser and EdgeWebView. This fixes issues with the IWebView2WebView members being out of scope when I save and reload the pointer.
author bsmith@81767d24-ef19-dc11-ae90-00e081727c95
date Sun, 24 Nov 2019 21:01:46 +0000
parents 638be5b17715
children 82e5c998df2e
files win/edge.cpp
diffstat 1 files changed, 312 insertions(+), 238 deletions(-) [+]
line wrap: on
line diff
--- a/win/edge.cpp	Sun Nov 24 06:59:52 2019 +0000
+++ b/win/edge.cpp	Sun Nov 24 21:01:46 2019 +0000
@@ -22,14 +22,293 @@
 extern "C" {
 
 	/* Import the character conversion functions from dw.c */
-	LPWSTR _myUTF8toWide(const char *utf8string, void *outbuf);
-	char *_myWideToUTF8(LPCWSTR widestring, void *outbuf);
+	LPWSTR _myUTF8toWide(const char* utf8string, void* outbuf);
+	char* _myWideToUTF8(LPCWSTR widestring, void* outbuf);
 	#define UTF8toWide(a) _myUTF8toWide(a, a ? _alloca(MultiByteToWideChar(CP_UTF8, 0, a, -1, NULL, 0) * sizeof(WCHAR)) : NULL)
 	#define WideToUTF8(a) _myWideToUTF8(a, a ? _alloca(WideCharToMultiByte(CP_UTF8, 0, a, -1, NULL, 0, NULL, NULL)) : NULL)
 	LRESULT CALLBACK _wndproc(HWND hWnd, UINT msg, WPARAM mp1, LPARAM mp2);
 	BOOL CALLBACK _free_window_memory(HWND handle, LPARAM lParam);
-	IWebView2Environment *DW_EDGE_ENV = NULL;
+}
+
+class EdgeBrowser
+{
+public:
+	LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
+	BOOL Detect(VOID);
+protected:
+	Microsoft::WRL::ComPtr<IWebView2Environment> Env;
+};
+
+class EdgeWebView
+{
+public:
+	VOID Action(int action);
+	int Raw(const char* string);
+	int URL(const char* url);
+	int JavascriptRun(const char* script, void* scriptdata);
+	VOID DoSize(VOID);
+	VOID Setup(HWND hwnd, IWebView2WebView* webview);
+	VOID Close(VOID);
+protected:
+	HWND hWnd = nullptr;
+	Microsoft::WRL::ComPtr<IWebView2WebView> WebView;
+};
+
+LRESULT CALLBACK EdgeBrowser::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+	switch (uMsg)
+	{
+	case WM_SIZE:
+	{
+		// Resize the browser object to fit the window
+		EdgeWebView *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 = (EdgeWebView*)dw_window_get_data(hWnd, _DW_HTML_DATA_NAME);
+		// Resize WebView to fit the bounds of the parent window
+		if (webview)
+			webview->DoSize();
+		return(0);
+	}
+
+	case WM_PAINT:
+	{
+		PAINTSTRUCT ps;
+		HDC hdc = BeginPaint(hWnd, &ps);
+		EndPaint(hWnd, &ps);
+		return(0);
+	}
+
+	case WM_CREATE:
+	{
+		// Step 3 - Create a single WebView within the parent window
+		// Create a WebView, whose parent is the main window hWnd
+		Env->CreateWebView(hWnd, Callback<IWebView2CreateWebViewCompletedHandler>(
+			[hWnd](HRESULT result, IWebView2WebView* webview) -> HRESULT {
+				EdgeWebView* WebView = new EdgeWebView;
+
+				WebView->Setup(hWnd, webview);
+				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
+				WebView->DoSize();
+
+				// Save the token, we might need to dw_window_set_data() this value
+				// for later use to remove the handlers
+				EventRegistrationToken token;
+
+				// Register a handler for the NavigationStarting event.
+				webview->add_NavigationStarting(
+					Callback<IWebView2NavigationStartingEventHandler>(
+						[hWnd](IWebView2WebView* sender,
+							IWebView2NavigationStartingEventArgs* args) -> HRESULT
+						{
+							LPWSTR uri;
+							sender->get_Source(&uri);
+
+							_wndproc(hWnd, WM_USER + 101, (WPARAM)DW_INT_TO_POINTER(DW_HTML_CHANGE_STARTED),
+								!wcscmp(uri, L"about:blank") ? (LPARAM)"" : (LPARAM)WideToUTF8((LPWSTR)uri));
+
+							return S_OK;
+						}).Get(), &token);
+
+				// Register a handler for the DocumentStateChanged event.
+				webview->add_DocumentStateChanged(
+					Callback<IWebView2DocumentStateChangedEventHandler>(
+						[hWnd](IWebView2WebView* sender,
+							IWebView2DocumentStateChangedEventArgs* args) -> HRESULT
+						{
+							LPWSTR uri;
+							sender->get_Source(&uri);
+
+							_wndproc(hWnd, WM_USER + 101, (WPARAM)DW_INT_TO_POINTER(DW_HTML_CHANGE_LOADING),
+								!wcscmp(uri, L"about:blank") ? (LPARAM)"" : (LPARAM)WideToUTF8((LPWSTR)uri));
+
+							return S_OK;
+						}).Get(), &token);
+
+				// Register a handler for the NavigationCompleted event.
+				webview->add_NavigationCompleted(
+					Callback<IWebView2NavigationCompletedEventHandler>(
+						[hWnd](IWebView2WebView* sender,
+							IWebView2NavigationCompletedEventArgs* args) -> HRESULT
+						{
+							LPWSTR uri;
+							sender->get_Source(&uri);
+
+							_wndproc(hWnd, WM_USER + 101, (WPARAM)DW_INT_TO_POINTER(DW_HTML_CHANGE_COMPLETE),
+								!wcscmp(uri, L"about:blank") ? (LPARAM)"" : (LPARAM)WideToUTF8((LPWSTR)uri));
+
+							return S_OK;
+						}).Get(), &token);
 
+				// Handle cached load requests due to delayed
+				// loading of the edge webview contexts
+				char *url = (char *)dw_window_get_data(hWnd, _DW_HTML_DATA_LOCATION);
+				if (url)
+				{
+					WebView->URL(url);
+					free((void*)url);
+				}
+				char *raw = (char *)dw_window_get_data(hWnd, _DW_HTML_DATA_RAW);
+				if (raw)
+				{
+					WebView->Raw(raw);
+					free((void*)raw);
+				}
+				return S_OK;
+			}).Get());
+		// Success
+		return(0);
+	}
+
+	case WM_DESTROY:
+	{
+		// Detach the browser object from this window, and free resources.
+		EdgeWebView *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 = (EdgeWebView*)dw_window_get_data(hWnd, _DW_HTML_DATA_NAME);
+		if (webview)
+		{
+			dw_window_set_data(hWnd, _DW_HTML_DATA_NAME, NULL);
+			webview->Close();
+			delete webview;
+		}
+		_free_window_memory(hWnd, 0);
+		return(TRUE);
+	}
+	}
+
+	return(DefWindowProc(hWnd, uMsg, wParam, lParam));
+}
+
+VOID EdgeWebView::DoSize(VOID)
+{
+	RECT bounds;
+
+	GetClientRect(hWnd, &bounds);
+	WebView->put_Bounds(bounds);
+}
+
+BOOL EdgeBrowser::Detect(VOID)
+{
+	CreateWebView2EnvironmentWithDetails(nullptr, nullptr, nullptr,
+		Callback<IWebView2CreateWebView2EnvironmentCompletedHandler>(
+			[this](HRESULT result, IWebView2Environment* env) -> HRESULT {
+				// Successfully created Edge environment, return TRUE 
+				Env = env;
+				return S_OK;
+			}).Get());
+	return Env ? TRUE : FALSE;
+}
+
+void EdgeWebView::Action(int action)
+{
+	// We want to get the base address (ie, a pointer) to the IWebView2WebView object embedded within the browser
+	// object, so we can call some of the functions in the former's table.
+	if (WebView)
+	{
+		// Call the desired function
+		switch (action)
+		{
+		case DW_HTML_GOBACK:
+		{
+			// Call the IWebView2WebView object's GoBack function.
+			WebView->GoBack();
+			break;
+		}
+
+		case DW_HTML_GOFORWARD:
+		{
+			// Call the IWebView2WebView object's GoForward function.
+			WebView->GoForward();
+			break;
+		}
+
+		case DW_HTML_GOHOME:
+		{
+			// Call the IWebView2WebView object's GoHome function.
+			dw_html_url(hWnd, (char*)DW_HOME_URL);
+			break;
+		}
+
+		case DW_HTML_SEARCH:
+		{
+			// Call the IWebView2WebView object's GoSearch function.
+			//WebView->GoSearch();
+			break;
+		}
+
+		case DW_HTML_RELOAD:
+		{
+			// Call the IWebView2WebView object's Refresh function.
+			WebView->Reload();
+		}
+
+		case DW_HTML_STOP:
+		{
+			// Call the IWebView2WebView object's Stop function.
+			//WebView->Stop();
+		}
+		}
+	}
+}
+
+int EdgeWebView::Raw(const char* string)
+{
+	if (WebView)
+		WebView->NavigateToString(UTF8toWide(string));
+	return DW_ERROR_NONE;
+}
+
+int EdgeWebView::URL(const char* url)
+{
+	if (WebView)
+		WebView->Navigate(UTF8toWide(url));
+	return DW_ERROR_NONE;
+}
+
+int EdgeWebView::JavascriptRun(const char* script, void* scriptdata)
+{
+	HWND thishwnd = hWnd;
+
+	if (WebView)
+		WebView->ExecuteScript(UTF8toWide(script),
+			Callback<IWebView2ExecuteScriptCompletedHandler>(
+				[thishwnd, scriptdata](HRESULT error, PCWSTR result) -> HRESULT
+				{
+					_wndproc(thishwnd, WM_USER + 100, (error == S_OK ? (WPARAM)WideToUTF8((LPWSTR)result) : NULL), (LPARAM)scriptdata);
+					return S_OK;
+				}).Get());
+	return DW_ERROR_NONE;
+}
+
+VOID EdgeWebView::Setup(HWND hwnd, IWebView2WebView* webview)
+{
+	hWnd = hwnd;
+	WebView = webview;
+}
+
+VOID EdgeWebView::Close(VOID)
+{
+	if (WebView)
+		WebView->Close();
+}
+
+EdgeBrowser *DW_EDGE = NULL;
+
+extern "C" {
 	/******************************* dw_edge_detect() **************************
 	 * Attempts to create a temporary Edge (Chromium) browser context...
 	 * If we succeed return TRUE and use Edge for HTML windows.
@@ -37,14 +316,18 @@
 	 */
 	BOOL _dw_edge_detect(VOID)
 	{
-		CreateWebView2EnvironmentWithDetails(nullptr, nullptr, nullptr,
-			Callback<IWebView2CreateWebView2EnvironmentCompletedHandler>(
-				[](HRESULT result, IWebView2Environment* env) -> HRESULT {
-					// Successfully created Edge environment, return TRUE 
-					DW_EDGE_ENV = env;
-					return S_OK;
-				}).Get());
-		return DW_EDGE_ENV ? TRUE : FALSE;
+		DW_EDGE = new EdgeBrowser;
+		if (DW_EDGE)
+		{
+			BOOL result = DW_EDGE->Detect();
+			if (!result)
+			{
+				delete DW_EDGE;
+				DW_EDGE = NULL;
+			}
+			return result;
+		}
+		return FALSE;
 	}
 
 	/******************************* dw_edge_action() **************************
@@ -63,60 +346,9 @@
 
 	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 IWebView2WebView object embedded within the browser
-		// object, so we can call some of the functions in the former's table.
+		EdgeWebView* webview = (EdgeWebView *)dw_window_get_data(hwnd, _DW_HTML_DATA_NAME);
 		if (webview)
-		{
-			// Call the desired function
-			switch (action)
-			{
-			case DW_HTML_GOBACK:
-			{
-				// Call the IWebView2WebView object's GoBack function.
-				webview->GoBack();
-				break;
-			}
-
-			case DW_HTML_GOFORWARD:
-			{
-				// Call the IWebView2WebView object's GoForward function.
-				webview->GoForward();
-				break;
-			}
-
-			case DW_HTML_GOHOME:
-			{
-				// Call the IWebView2WebView object's GoHome function.
-				dw_html_url(hwnd, (char *)DW_HOME_URL);
-				break;
-			}
-
-			case DW_HTML_SEARCH:
-			{
-				// Call the IWebView2WebView object's GoSearch function.
-				//webview->GoSearch();
-				break;
-			}
-
-			case DW_HTML_RELOAD:
-			{
-				// Call the IWebView2WebView object's Refresh function.
-				webview->Reload();
-			}
-
-			case DW_HTML_STOP:
-			{
-				// Call the IWebView2WebView object's Stop function.
-				//webview->Stop();
-			}
-			}
-		}
+			webview->Action(action);
 	}
 
 	/******************************* dw_edge_raw() ****************************
@@ -132,18 +364,13 @@
 	 * RETURNS: 0 if success, or non-zero if an error.
 	 */
 
-	int _dw_edge_raw(HWND hwnd, const char *string)
+	int _dw_edge_raw(HWND hwnd, const char* 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);
-
+		EdgeWebView* webview = (EdgeWebView*)dw_window_get_data(hwnd, _DW_HTML_DATA_NAME);
 		if (webview)
-			webview->NavigateToString(UTF8toWide(string));
+			return webview->Raw(string);
 		else
-			dw_window_set_data(hwnd, _DW_HTML_DATA_RAW, _wcsdup(UTF8toWide(string)));
+			dw_window_set_data(hwnd, _DW_HTML_DATA_RAW, strdup(string));
 		return DW_ERROR_NONE;
 	}
 
@@ -156,18 +383,13 @@
 	 * RETURNS: 0 if success, or non-zero if an error.
 	 */
 
-	int _dw_edge_url(HWND hwnd, const char *url)
+	int _dw_edge_url(HWND hwnd, const char* 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);
-
+		EdgeWebView* webview = (EdgeWebView*)dw_window_get_data(hwnd, _DW_HTML_DATA_NAME);
 		if (webview)
-			webview->Navigate(UTF8toWide(url));
+			return webview->URL(url);
 		else
-			dw_window_set_data(hwnd, _DW_HTML_DATA_LOCATION, _wcsdup(UTF8toWide(url)));
+			dw_window_set_data(hwnd, _DW_HTML_DATA_LOCATION, strdup(url));
 		return DW_ERROR_NONE;
 	}
 
@@ -181,23 +403,12 @@
 	 * RETURNS: 0 if success, or non-zero if an error.
 	 */
 
-	int _dw_edge_javascript_run(HWND hwnd, const char *script, void *scriptdata)
+	int _dw_edge_javascript_run(HWND hwnd, const char* script, void* scriptdata)
 	{
-		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);
-
+		EdgeWebView* webview = (EdgeWebView*)dw_window_get_data(hwnd, _DW_HTML_DATA_NAME);
 		if (webview)
-			webview->ExecuteScript(UTF8toWide(script),
-				Callback<IWebView2ExecuteScriptCompletedHandler>(
-					[hwnd, scriptdata](HRESULT error, PCWSTR result) -> HRESULT
-					{
-						_wndproc(hwnd, WM_USER + 100, (error == S_OK ? (WPARAM)WideToUTF8((LPWSTR)result) : NULL), (LPARAM)scriptdata);
-						return S_OK;
-					}).Get());
-		return DW_ERROR_NONE;
+			return webview->JavascriptRun(script, scriptdata);
+		return DW_ERROR_UNKNOWN;
 	}
 
 	/************************** edgeWindowProc() *************************
@@ -206,145 +417,8 @@
 
 	LRESULT CALLBACK _edgeWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
 	{
-		switch (uMsg)
-		{
-		case WM_SIZE:
-		{
-			// Resize the browser object to fit the window
-			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);
-			// Resize WebView to fit the bounds of the parent window
-			if (webview)
-			{
-				RECT bounds;
-
-				GetClientRect(hWnd, &bounds);
-				webview->put_Bounds(bounds);
-			}
-			return(0);
-		}
-
-		case WM_PAINT:
-		{
-			PAINTSTRUCT ps;
-			HDC hdc = BeginPaint(hWnd, &ps);
-			EndPaint(hWnd, &ps);
-			return(0);
-		}
-
-		case WM_CREATE:
-		{
-			// Step 3 - Create a single WebView within the parent window
-			// Create a WebView, whose parent is the main window hWnd
-			DW_EDGE_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);
-
-					// Save the token, we might need to dw_window_set_data() this value
-					// for later use to remove the handlers
-					EventRegistrationToken token;
-
-					// Register a handler for the NavigationStarting event.
-					webview->add_NavigationStarting(
-						Callback<IWebView2NavigationStartingEventHandler>(
-							[hWnd](IWebView2WebView* sender,
-								IWebView2NavigationStartingEventArgs* args) -> HRESULT
-							{
-								LPWSTR uri;
-								sender->get_Source(&uri);
-
-								_wndproc(hWnd, WM_USER + 101, (WPARAM)DW_INT_TO_POINTER(DW_HTML_CHANGE_STARTED),
-									!wcscmp(uri, L"about:blank") ? (LPARAM)"" : (LPARAM)WideToUTF8((LPWSTR)uri));
-
-								return S_OK;
-							}).Get(), &token);
-
-					// Register a handler for the DocumentStateChanged event.
-					webview->add_DocumentStateChanged(
-						Callback<IWebView2DocumentStateChangedEventHandler>(
-							[hWnd](IWebView2WebView* sender,
-								IWebView2DocumentStateChangedEventArgs* args) -> HRESULT
-							{
-								LPWSTR uri;
-								sender->get_Source(&uri);
-
-								_wndproc(hWnd, WM_USER + 101, (WPARAM)DW_INT_TO_POINTER(DW_HTML_CHANGE_LOADING), 
-									!wcscmp(uri, L"about:blank") ? (LPARAM)"" : (LPARAM)WideToUTF8((LPWSTR)uri));
-
-								return S_OK;
-							}).Get(), &token);
-
-					// Register a handler for the NavigationCompleted event.
-					webview->add_NavigationCompleted(
-						Callback<IWebView2NavigationCompletedEventHandler>(
-							[hWnd](IWebView2WebView* sender,
-								IWebView2NavigationCompletedEventArgs* args) -> HRESULT
-							{
-								LPWSTR uri;
-								sender->get_Source(&uri);
-
-								_wndproc(hWnd, WM_USER + 101, (WPARAM)DW_INT_TO_POINTER(DW_HTML_CHANGE_COMPLETE),
-									!wcscmp(uri, L"about:blank") ? (LPARAM)"" : (LPARAM)WideToUTF8((LPWSTR)uri));
-
-								return S_OK;
-							}).Get(), &token);
-
-					// Handle cached load requests due to delayed
-					// loading of the edge webview contexts
-					LPCWSTR url = (LPCWSTR)dw_window_get_data(hWnd, _DW_HTML_DATA_LOCATION);
-					if(url)
-					{
-						webview->Navigate(url);
-						free((void *)url);
-					}
-					LPCWSTR raw = (LPCWSTR)dw_window_get_data(hWnd, _DW_HTML_DATA_RAW);
-					if (raw)
-					{
-						webview->NavigateToString(raw);
-						free((void *)raw);
-					}
-					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();
-			}
-			_free_window_memory(hWnd, 0);
-			return(TRUE);
-		}
-		}
-
-		return(DefWindowProc(hWnd, uMsg, wParam, lParam));
+		if (DW_EDGE)
+			return DW_EDGE->WndProc(hWnd, uMsg, wParam, lParam);
+		return DefWindowProc(hWnd, uMsg, wParam, lParam);
 	}
-}
+}
\ No newline at end of file