# HG changeset patch # User bsmith@81767d24-ef19-dc11-ae90-00e081727c95 # Date 1574629306 0 # Node ID 0d8b898b03e2e498e5d66ad88a1d3ee30b7f58fb # Parent 638be5b17715c07ba8f04fbee77b2b96f3bc4d6d 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. diff -r 638be5b17715 -r 0d8b898b03e2 win/edge.cpp --- 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 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 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( + [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( + [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( + [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( + [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( + [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( + [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( - [](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( - [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( - [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( - [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( - [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( - [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