diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..c0cef7a --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ + +# Default ignored files +/.idea.WebWindow.Dev/.idea/workspace.xml \ No newline at end of file diff --git a/src/WebWindow.Native/Exports.cpp b/src/WebWindow.Native/Exports.cpp index 1b62fbd..e517f13 100644 --- a/src/WebWindow.Native/Exports.cpp +++ b/src/WebWindow.Native/Exports.cpp @@ -134,4 +134,9 @@ extern "C" { instance->SetIconFile(filename); } + + EXPORTED void WebWindow_SetUriChangeCallback(WebWindow* instance, UriChangeCallback callback) + { + instance->SetUriChangeCallback(callback); + } } diff --git a/src/WebWindow.Native/WebWindow.Linux.cpp b/src/WebWindow.Native/WebWindow.Linux.cpp index 8ec24c1..900d9f7 100644 --- a/src/WebWindow.Native/WebWindow.Linux.cpp +++ b/src/WebWindow.Native/WebWindow.Linux.cpp @@ -73,6 +73,21 @@ void HandleWebMessage(WebKitUserContentManager* contentManager, WebKitJavascript webkit_javascript_result_unref(jsResult); } +void HandleUriChange(GObject* object, WebKitLoadEvent event, gpointer user_data) +{ + WebKitWebView *web_view; + + const gchar *uri; + + if (event == WEBKIT_LOAD_FINISHED) { + web_view = WEBKIT_WEB_VIEW(object); + uri = webkit_web_view_get_uri(web_view); + + UriChangeCallback callback = (UriChangeCallback)user_data; + callback(AutoString(uri)); + } +} + void WebWindow::Show() { if (!_webview) @@ -99,6 +114,7 @@ void WebWindow::Show() g_signal_connect(contentManager, "script-message-received::webwindowinterop", G_CALLBACK(HandleWebMessage), (void*)_webMessageReceivedCallback); + g_signal_connect(_webview, "load-changed", G_CALLBACK(HandleUriChange), (void*)_uriChangeCallback); webkit_user_content_manager_register_script_message_handler(contentManager, "webwindowinterop"); } diff --git a/src/WebWindow.Native/WebWindow.Mac.NavigationDelegate.h b/src/WebWindow.Native/WebWindow.Mac.NavigationDelegate.h new file mode 100644 index 0000000..046c29e --- /dev/null +++ b/src/WebWindow.Native/WebWindow.Mac.NavigationDelegate.h @@ -0,0 +1,13 @@ +#import +#import +#include "WebWindow.h" + +typedef void (*UriChangedCallback) (char* message); + +@interface MyNavigationDelegate : NSObject { + @public + NSWindow * window; + WebWindow * webWindow; + UriChangedCallback uriChangedCallback; +} +@end \ No newline at end of file diff --git a/src/WebWindow.Native/WebWindow.Mac.NavigationDelegate.mm b/src/WebWindow.Native/WebWindow.Mac.NavigationDelegate.mm new file mode 100644 index 0000000..1498d99 --- /dev/null +++ b/src/WebWindow.Native/WebWindow.Mac.NavigationDelegate.mm @@ -0,0 +1,26 @@ +#import "WebWindow.Mac.NavigationDelegate.h" + +@implementation MyNavigationDelegate : NSObject + +- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation; +{ + [self callUriChangedCallback:webView.URL.absoluteString]; +} + +- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error; +{ + [self callUriChangedCallback:webView.URL.absoluteString]; +} + +- (void)webView:(WKWebView *)webView +didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation; +{ + [self callUriChangedCallback:webView.URL.absoluteString]; +} + +- (void) callUriChangedCallback: (NSString *) uri; +{ + char* writableUri = (char*)[uri UTF8String]; + uriChangedCallback(writableUri); +} +@end diff --git a/src/WebWindow.Native/WebWindow.Mac.mm b/src/WebWindow.Native/WebWindow.Mac.mm index cdee0bf..0e20bc0 100644 --- a/src/WebWindow.Native/WebWindow.Mac.mm +++ b/src/WebWindow.Native/WebWindow.Mac.mm @@ -3,6 +3,7 @@ #import "WebWindow.Mac.AppDelegate.h" #import "WebWindow.Mac.UiDelegate.h" #import "WebWindow.Mac.UrlSchemeHandler.h" +#import "WebWindow.Mac.NavigationDelegate.h" #include #include #import @@ -69,6 +70,8 @@ MyUiDelegate *uiDelegate = [[[MyUiDelegate alloc] init] autorelease]; uiDelegate->webWindow = this; + MyNavigationDelegate *navDelegate = [[[MyNavigationDelegate alloc] init] autorelease]; + NSString *initScriptSource = @"window.__receiveMessageCallbacks = [];" "window.__dispatchMessageCallback = function(message) {" " window.__receiveMessageCallbacks.forEach(function(callback) { callback(message); });" @@ -92,13 +95,17 @@ [webView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; [window.contentView addSubview:webView]; [window.contentView setAutoresizesSubviews:YES]; - uiDelegate->window = window; webView.UIDelegate = uiDelegate; - uiDelegate->webMessageReceivedCallback = _webMessageReceivedCallback; [userContentController addScriptMessageHandler:uiDelegate name:@"webwindowinterop"]; + navDelegate->window = window; + navDelegate->webWindow = this; + navDelegate->uriChangedCallback = _uriChangeCallback; + + webView.navigationDelegate = navDelegate; + // TODO: Remove these observers when the window is closed [[NSNotificationCenter defaultCenter] addObserver:uiDelegate selector:@selector(windowDidResize:) name:NSWindowDidResizeNotification object:window]; [[NSNotificationCenter defaultCenter] addObserver:uiDelegate selector:@selector(windowDidMove:) name:NSWindowDidMoveNotification object:window]; diff --git a/src/WebWindow.Native/WebWindow.Native.vcxproj b/src/WebWindow.Native/WebWindow.Native.vcxproj index 3c5ef82..c9ccff4 100644 --- a/src/WebWindow.Native/WebWindow.Native.vcxproj +++ b/src/WebWindow.Native/WebWindow.Native.vcxproj @@ -153,10 +153,14 @@ + + + + diff --git a/src/WebWindow.Native/WebWindow.Windows.cpp b/src/WebWindow.Native/WebWindow.Windows.cpp index f56db8c..2980038 100644 --- a/src/WebWindow.Native/WebWindow.Windows.cpp +++ b/src/WebWindow.Native/WebWindow.Windows.cpp @@ -225,7 +225,18 @@ void WebWindow::AttachWebView() Settings->put_IsScriptEnabled(TRUE); Settings->put_AreDefaultScriptDialogsEnabled(TRUE); Settings->put_IsWebMessageEnabled(TRUE); - + // Add a navigation change event handler + EventRegistrationToken token; + _webviewWindow->add_NavigationStarting(Callback( + [this](IWebView2WebView* webview, IWebView2NavigationStartingEventArgs * args) -> HRESULT + { + wil::unique_cotaskmem_string uri; + args->get_Uri(&uri); + _uriChangeCallback(uri.get()); + return S_OK; + } + ).Get(), &token); + // Register interop APIs EventRegistrationToken webMessageToken; _webviewWindow->AddScriptToExecuteOnDocumentCreated(L"window.external = { sendMessage: function(message) { window.chrome.webview.postMessage(message); }, receiveMessage: function(callback) { window.chrome.webview.addEventListener(\'message\', function(e) { callback(e.data); }); } };", nullptr); diff --git a/src/WebWindow.Native/WebWindow.h b/src/WebWindow.Native/WebWindow.h index f5cf4f7..3fe4669 100644 --- a/src/WebWindow.Native/WebWindow.h +++ b/src/WebWindow.Native/WebWindow.h @@ -31,6 +31,7 @@ typedef void* (*WebResourceRequestedCallback)(AutoString url, int* outNumBytes, typedef int (*GetAllMonitorsCallback)(const Monitor* monitor); typedef void (*ResizedCallback)(int width, int height); typedef void (*MovedCallback)(int x, int y); +typedef void (*UriChangeCallback)(AutoString url); class WebWindow { @@ -38,6 +39,7 @@ class WebWindow WebMessageReceivedCallback _webMessageReceivedCallback; MovedCallback _movedCallback; ResizedCallback _resizedCallback; + UriChangeCallback _uriChangeCallback; #ifdef _WIN32 static HINSTANCE _hInstance; HWND _hWnd; @@ -87,6 +89,8 @@ class WebWindow void SetPosition(int x, int y); void SetMovedCallback(MovedCallback callback) { _movedCallback = callback; } void InvokeMoved(int x, int y) { if (_movedCallback) _movedCallback(x, y); } + void SetUriChangeCallback(UriChangeCallback callback) { _uriChangeCallback = callback; } + void InvokeUriChange(AutoString uri) { if (_uriChangeCallback) _uriChangeCallback(uri); } void SetTopmost(bool topmost); void SetIconFile(AutoString filename); }; diff --git a/src/WebWindow/WebWindow.cs b/src/WebWindow/WebWindow.cs index 49e3b5a..fa62061 100644 --- a/src/WebWindow/WebWindow.cs +++ b/src/WebWindow/WebWindow.cs @@ -55,7 +55,7 @@ public class WebWindow [UnmanagedFunctionPointer(CallingConvention.Cdecl)] delegate int GetAllMonitorsCallback(in NativeMonitor monitor); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] delegate void ResizedCallback(int width, int height); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] delegate void MovedCallback(int x, int y); - + [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Auto)] delegate void UriChangeCallback(string uri); const string DllName = "WebWindow.Native"; [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] static extern IntPtr WebWindow_register_win32(IntPtr hInstance); [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] static extern IntPtr WebWindow_register_mac(); @@ -82,6 +82,7 @@ public class WebWindow [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] static extern void WebWindow_SetMovedCallback(IntPtr instance, MovedCallback callback); [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] static extern void WebWindow_SetTopmost(IntPtr instance, int topmost); [DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)] static extern void WebWindow_SetIconFile(IntPtr instance, string filename); + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)] static extern void WebWindow_SetUriChangeCallback(IntPtr instance, UriChangeCallback callback); private readonly List _gcHandlesToFree = new List(); private readonly List _hGlobalToFree = new List(); @@ -145,6 +146,10 @@ public WebWindow(string title, Action configure) _gcHandlesToFree.Add(GCHandle.Alloc(onMovedDelegate)); WebWindow_SetMovedCallback(_nativeWebWindow, onMovedDelegate); + var onUriChangeDelegate = (UriChangeCallback)UriChanged; + _gcHandlesToFree.Add(GCHandle.Alloc(onUriChangeDelegate)); + WebWindow_SetUriChangeCallback(_nativeWebWindow, onUriChangeDelegate); + // Auto-show to simplify the API, but more importantly because you can't // do things like navigate until it has been shown Show(); @@ -237,6 +242,10 @@ public void SendMessage(string message) } public event EventHandler OnWebMessageReceived; + + private void UriChanged(string uri) => OnUriChange?.Invoke(this, uri); + + public event EventHandler OnUriChange; private void WriteTitleField(string value) { diff --git a/src/WebWindow/WebWindow.csproj b/src/WebWindow/WebWindow.csproj index f2a56da..dfffaaf 100644 --- a/src/WebWindow/WebWindow.csproj +++ b/src/WebWindow/WebWindow.csproj @@ -20,7 +20,7 @@ + Command="gcc -shared -lstdc++ -DOS_MAC -framework Cocoa -framework WebKit WebWindow.Mac.mm Exports.cpp WebWindow.Mac.AppDelegate.mm WebWindow.Mac.UiDelegate.mm WebWindow.Mac.UrlSchemeHandler.m WebWindow.Mac.NavigationDelegate.mm -o x64/$(Configuration)/WebWindow.Native.dylib" /> diff --git a/src/WebWindow/WebWindowOptions.cs b/src/WebWindow/WebWindowOptions.cs index 17b96cd..b49a75d 100644 --- a/src/WebWindow/WebWindowOptions.cs +++ b/src/WebWindow/WebWindowOptions.cs @@ -9,6 +9,7 @@ public class WebWindowOptions public IDictionary SchemeHandlers { get; } = new Dictionary(); + } public delegate Stream ResolveWebResourceDelegate(string url, out string contentType); diff --git a/testassets/HelloWorldApp/Program.cs b/testassets/HelloWorldApp/Program.cs index c5fbf29..1baeb9e 100644 --- a/testassets/HelloWorldApp/Program.cs +++ b/testassets/HelloWorldApp/Program.cs @@ -22,7 +22,10 @@ static void Main(string[] args) { window.SendMessage("Got message: " + message); }; - + window.OnUriChange += (sender, uri) => + { + Console.WriteLine($"New URI: {uri}"); + }; window.NavigateToLocalFile("wwwroot/index.html"); window.WaitForExit(); } diff --git a/testassets/HelloWorldApp/wwwroot/index.html b/testassets/HelloWorldApp/wwwroot/index.html index bda55ce..e37c78e 100644 --- a/testassets/HelloWorldApp/wwwroot/index.html +++ b/testassets/HelloWorldApp/wwwroot/index.html @@ -7,6 +7,10 @@

Hello

+ +

+ Link to example.com +