diff --git a/v3/examples/raw-message/main.go b/v3/examples/raw-message/main.go index cd879ea5cca..69777337391 100644 --- a/v3/examples/raw-message/main.go +++ b/v3/examples/raw-message/main.go @@ -3,8 +3,10 @@ package main import ( "embed" _ "embed" - "github.com/wailsapp/wails/v3/pkg/application" + "fmt" "log" + + "github.com/wailsapp/wails/v3/pkg/application" ) //go:embed assets @@ -21,8 +23,8 @@ func main() { Mac: application.MacOptions{ ApplicationShouldTerminateAfterLastWindowClosed: true, }, - RawMessageHandler: func(window application.Window, message string) { - println("Raw message received from Window '" + window.Name() + "' with message: " + message) + RawMessageHandler: func(window application.Window, message string, originInfo *application.OriginInfo) { + println(fmt.Sprintf("Raw message received from Window %s with message: %s, origin %s, topOrigin %s, isMainFrame %t", window.Name(), message, originInfo.Origin, originInfo.TopOrigin, originInfo.IsMainFrame)) }, }) diff --git a/v3/pkg/application/application.go b/v3/pkg/application/application.go index 022d69a6f05..fe1fdbd681d 100644 --- a/v3/pkg/application/application.go +++ b/v3/pkg/application/application.go @@ -211,8 +211,15 @@ type ( // Messages sent from javascript get routed here type windowMessage struct { - windowId uint - message string + windowId uint + message string + originInfo *OriginInfo +} + +type OriginInfo struct { + Origin string + TopOrigin string + IsMainFrame bool } var windowMessageBuffer = make(chan *windowMessage, 5) @@ -708,7 +715,7 @@ func (a *App) handleWindowMessage(event *windowMessage) { window.HandleMessage(event.message) } else { if a.options.RawMessageHandler != nil { - a.options.RawMessageHandler(window, event.message) + a.options.RawMessageHandler(window, event.message, event.originInfo) } } } diff --git a/v3/pkg/application/application_darwin.go b/v3/pkg/application/application_darwin.go index 31459ff55c0..1d8d4c402c3 100644 --- a/v3/pkg/application/application_darwin.go +++ b/v3/pkg/application/application_darwin.go @@ -344,10 +344,18 @@ func processWindowEvent(windowID C.uint, eventID C.uint) { } //export processMessage -func processMessage(windowID C.uint, message *C.char) { +func processMessage(windowID C.uint, message *C.char, origin *C.char, isMainFrame bool) { + o := "" + if origin != nil { + o = C.GoString(origin) + } windowMessageBuffer <- &windowMessage{ windowId: uint(windowID), message: C.GoString(message), + originInfo: &OriginInfo{ + Origin: o, + IsMainFrame: isMainFrame, + }, } } diff --git a/v3/pkg/application/application_options.go b/v3/pkg/application/application_options.go index a9f8cf2f429..01586258dcd 100644 --- a/v3/pkg/application/application_options.go +++ b/v3/pkg/application/application_options.go @@ -88,7 +88,7 @@ type Options struct { // RawMessageHandler is called when the frontend sends a raw message. // This is useful for implementing custom frontend-to-backend communication. - RawMessageHandler func(window Window, message string) + RawMessageHandler func(window Window, message string, originInfo *OriginInfo) // WarningHandler is called when a warning occurs WarningHandler func(string) diff --git a/v3/pkg/application/linux_cgo.go b/v3/pkg/application/linux_cgo.go index 3fbb543c68a..e4ebb06b07b 100644 --- a/v3/pkg/application/linux_cgo.go +++ b/v3/pkg/application/linux_cgo.go @@ -62,6 +62,16 @@ static void save_window_id(void *object, uint value) g_object_set_data((GObject *)object, "windowid", GUINT_TO_POINTER((guint)value)); } +static void save_webview_to_content_manager(void *contentManager, void *webview) +{ + g_object_set_data(G_OBJECT((WebKitUserContentManager *)contentManager), "webview", webview); +} + +static WebKitWebView* get_webview_from_content_manager(void *contentManager) +{ + return WEBKIT_WEB_VIEW(g_object_get_data(G_OBJECT(contentManager), "webview")); +} + static guint get_window_id(void *object) { return GPOINTER_TO_UINT(g_object_get_data((GObject *)object, "windowid")); @@ -1109,6 +1119,8 @@ func windowNewWebview(parentId uint, gpuPolicy WebviewGpuPolicy) pointer { C.webkit_user_content_manager_register_script_message_handler(manager, c.String("external")) webView := C.webkit_web_view_new_with_user_content_manager(manager) + C.save_webview_to_content_manager(unsafe.Pointer(manager), unsafe.Pointer(webView)) + // attach window id to both the webview and contentmanager C.save_window_id(unsafe.Pointer(webView), C.uint(parentId)) C.save_window_id(unsafe.Pointer(manager), C.uint(parentId)) @@ -1646,6 +1658,17 @@ func sendMessageToBackend(contentManager *C.WebKitUserContentManager, result *C. // Get the windowID from the contentManager thisWindowID := uint(C.get_window_id(unsafe.Pointer(contentManager))) + webView := C.get_webview_from_content_manager(unsafe.Pointer(contentManager)) + var origin string + if webView != nil { + currentUri := C.webkit_web_view_get_uri(webView) + if currentUri != nil { + uri := C.g_strdup(currentUri) + defer C.g_free(C.gpointer(uri)) + origin = C.GoString(uri) + } + } + var msg string value := C.webkit_javascript_result_get_js_value(result) message := C.jsc_value_to_string(value) @@ -1654,6 +1677,9 @@ func sendMessageToBackend(contentManager *C.WebKitUserContentManager, result *C. windowMessageBuffer <- &windowMessage{ windowId: thisWindowID, message: msg, + originInfo: &OriginInfo{ + Origin: origin, + }, } } diff --git a/v3/pkg/application/webview_window_darwin.m b/v3/pkg/application/webview_window_darwin.m index 1a4546d7436..fb7aced0527 100644 --- a/v3/pkg/application/webview_window_darwin.m +++ b/v3/pkg/application/webview_window_darwin.m @@ -4,7 +4,7 @@ #import #import "webview_window_darwin.h" #import "../events/events_darwin.h" -extern void processMessage(unsigned int, const char*); +extern void processMessage(unsigned int, const char*, const char *, bool); extern void processURLRequest(unsigned int, void *); extern void processDragItems(unsigned int windowId, char** arr, int length, int x, int y); extern void processWindowKeyDownEvent(unsigned int, const char*); @@ -272,7 +272,7 @@ - (BOOL)prepareForDragOperation:(id)sender { - (BOOL)performDragOperation:(id)sender { NSLog(@"WebviewWindowDelegate: performDragOperation called. WindowID: %u", self.windowId); NSPasteboard *pasteboard = [sender draggingPasteboard]; - + if (hasListeners(EventWindowFileDraggingPerformed)) { processWindowEvent(self.windowId, EventWindowFileDraggingPerformed); } @@ -295,13 +295,13 @@ - (BOOL)performDragOperation:(id)sender { NSLog(@"WebviewWindowDelegate: performDragOperation - File %lu: %@", (unsigned long)i, str); cArray[i] = (char*)[str UTF8String]; } - + // Get the WebviewWindow instance, which is the dragging destination WebviewWindow *window = (WebviewWindow *)[sender draggingDestinationWindow]; WKWebView *webView = window.webView; // Get the webView from the window NSPoint dropPointInWindow = [sender draggingLocation]; NSPoint dropPointInView = [webView convertPoint:dropPointInWindow fromView:nil]; // Convert to webView's coordinate system - + CGFloat viewHeight = webView.frame.size.height; int x = (int)dropPointInView.x; int y = (int)(viewHeight - dropPointInView.y); // Flip Y for web coordinate system @@ -336,9 +336,22 @@ - (void) startDrag:(WebviewWindow*)window { } // Handle script messages from the external bridge - (void)userContentController:(nonnull WKUserContentController *)userContentController didReceiveScriptMessage:(nonnull WKScriptMessage *)message { - NSString *m = message.body; + // Get the origin from the message's frame + NSString *origin = nil; + if (message.frameInfo && message.frameInfo.request && message.frameInfo.request.URL) { + NSURL *url = message.frameInfo.request.URL; + if (url.scheme && url.host) { + origin = [url absoluteString]; + } + } + + id body = message.body; + NSString *m = [body isKindOfClass:[NSString class]] ? (NSString *)body : [body description]; const char *_m = [m UTF8String]; - processMessage(self.windowId, _m); + + const char *_origin = origin ? [origin UTF8String] : ""; + + processMessage(self.windowId, _m, _origin, message.frameInfo.isMainFrame); } - (void)handleLeftMouseDown:(NSEvent *)event { self.leftMouseEvent = event; @@ -782,25 +795,25 @@ - (void)webView:(nonnull WKWebView *)webview didCommitNavigation:(WKNavigation * void windowSetScreen(void* window, void* screen, int yOffset) { WebviewWindow* nsWindow = (WebviewWindow*)window; NSScreen* nsScreen = (NSScreen*)screen; - + // Get current frame NSRect frame = [nsWindow frame]; - + // Convert frame to screen coordinates NSRect screenFrame = [nsScreen frame]; NSRect currentScreenFrame = [[nsWindow screen] frame]; - + // Calculate the menubar height for the target screen NSRect visibleFrame = [nsScreen visibleFrame]; CGFloat menubarHeight = screenFrame.size.height - visibleFrame.size.height; - + // Calculate the distance from the top of the current screen CGFloat topOffset = currentScreenFrame.origin.y + currentScreenFrame.size.height - frame.origin.y; - + // Position relative to new screen's top, accounting for menubar frame.origin.x = screenFrame.origin.x + (frame.origin.x - currentScreenFrame.origin.x); frame.origin.y = screenFrame.origin.y + screenFrame.size.height - topOffset - menubarHeight - yOffset; - + // Set the frame which moves the window to the new screen [nsWindow setFrame:frame display:YES]; } @@ -816,13 +829,13 @@ bool isLiquidGlassSupported() { void windowRemoveVisualEffects(void* nsWindow) { WebviewWindow* window = (WebviewWindow*)nsWindow; NSView* contentView = [window contentView]; - + // Get NSGlassEffectView class if available (avoid hard reference) Class glassEffectViewClass = nil; if (@available(macOS 26.0, *)) { glassEffectViewClass = NSClassFromString(@"NSGlassEffectView"); } - + // Remove all NSVisualEffectView and NSGlassEffectView subviews NSArray* subviews = [contentView subviews]; for (NSView* subview in subviews) { @@ -836,13 +849,13 @@ void windowRemoveVisualEffects(void* nsWindow) { void configureWebViewForLiquidGlass(void* nsWindow) { WebviewWindow* window = (WebviewWindow*)nsWindow; WKWebView* webView = window.webView; - + // Make WebView background transparent [webView setValue:@NO forKey:@"drawsBackground"]; if (@available(macOS 10.12, *)) { [webView setValue:[NSColor clearColor] forKey:@"backgroundColor"]; } - + // Ensure WebView is above glass layer if (webView.layer) { webView.layer.zPosition = 1.0; @@ -852,10 +865,10 @@ void configureWebViewForLiquidGlass(void* nsWindow) { } // Apply Liquid Glass effect to window void windowSetLiquidGlass(void* nsWindow, int style, int material, double cornerRadius, - int r, int g, int b, int a, + int r, int g, int b, int a, const char* groupID, double groupSpacing) { WebviewWindow* window = (WebviewWindow*)nsWindow; - + // Ensure we're on the main thread for UI operations if (![NSThread isMainThread]) { dispatch_sync(dispatch_get_main_queue(), ^{ @@ -863,38 +876,38 @@ void windowSetLiquidGlass(void* nsWindow, int style, int material, double corner }); return; } - + // Remove any existing visual effects windowRemoveVisualEffects(nsWindow); - + // Try to use NSGlassEffectView if available NSView* glassView = nil; - + if (@available(macOS 26.0, *)) { Class NSGlassEffectViewClass = NSClassFromString(@"NSGlassEffectView"); if (NSGlassEffectViewClass) { // Create NSGlassEffectView (autoreleased) glassView = [[[NSGlassEffectViewClass alloc] init] autorelease]; - + // Set corner radius if the property exists if (cornerRadius > 0 && [glassView respondsToSelector:@selector(setCornerRadius:)]) { [glassView setValue:@(cornerRadius) forKey:@"cornerRadius"]; } - + // Set tint color if the property exists and color is specified if (a > 0 && [glassView respondsToSelector:@selector(setTintColor:)]) { NSColor* tintColor = [NSColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:a/255.0]; // Use performSelector to safely set tintColor if the setter exists [glassView performSelector:@selector(setTintColor:) withObject:tintColor]; } - + // Set style if the property exists if ([glassView respondsToSelector:@selector(setStyle:)]) { // For vibrant style, try to use Light style for a lighter effect int lightStyle = (style == LiquidGlassStyleVibrant) ? LiquidGlassStyleLight : style; [glassView setValue:@(lightStyle) forKey:@"style"]; } - + // Set group identifier if the property exists and groupID is specified if (groupID && strlen(groupID) > 0) { if ([glassView respondsToSelector:@selector(setGroupIdentifier:)]) { @@ -905,19 +918,19 @@ void windowSetLiquidGlass(void* nsWindow, int style, int material, double corner [glassView performSelector:@selector(setGroupName:) withObject:groupIDString]; } } - + // Set group spacing if the property exists and spacing is specified if (groupSpacing > 0 && [glassView respondsToSelector:@selector(setGroupSpacing:)]) { [glassView setValue:@(groupSpacing) forKey:@"groupSpacing"]; } } } - + // Fallback to NSVisualEffectView if NSGlassEffectView is not available if (!glassView) { NSVisualEffectView* effectView = [[[NSVisualEffectView alloc] init] autorelease]; glassView = effectView; // Use effectView as glassView for the rest of the function - + // If a custom material is specified, use it directly if (material >= 0) { [effectView setMaterial:(NSVisualEffectMaterial)material]; @@ -968,15 +981,15 @@ void windowSetLiquidGlass(void* nsWindow, int style, int material, double corner break; } } - + // Use followsWindowActiveState for automatic adjustment [effectView setState:NSVisualEffectStateFollowsWindowActiveState]; - + // Don't emphasize - it makes the effect too dark if (@available(macOS 10.12, *)) { [effectView setEmphasized:NO]; } - + // Apply corner radius if specified if (cornerRadius > 0) { [effectView setWantsLayer:YES]; @@ -984,30 +997,30 @@ void windowSetLiquidGlass(void* nsWindow, int style, int material, double corner effectView.layer.masksToBounds = YES; } } - + // Get the content view NSView* contentView = [window contentView]; - + // Set up the glass view [glassView setFrame:contentView.bounds]; [glassView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; - + // Check if this is a real NSGlassEffectView with contentView property BOOL hasContentView = [glassView respondsToSelector:@selector(contentView)]; - + if (hasContentView) { // NSGlassEffectView: Add it to window and webView goes in its contentView [contentView addSubview:glassView positioned:NSWindowBelow relativeTo:nil]; - + // Safely reparent the webView to the glass view's contentView WKWebView* webView = window.webView; NSView* glassContentView = [glassView valueForKey:@"contentView"]; - + // Only proceed if both webView and glassContentView are non-nil if (webView && glassContentView) { // Always remove from current superview to avoid exceptions [webView removeFromSuperview]; - + // Add to the glass view's contentView [glassContentView addSubview:webView]; [webView setFrame:glassContentView.bounds]; @@ -1016,17 +1029,17 @@ void windowSetLiquidGlass(void* nsWindow, int style, int material, double corner } else { // NSVisualEffectView: Add glass as bottom layer, webView on top [contentView addSubview:glassView positioned:NSWindowBelow relativeTo:nil]; - + WKWebView* webView = window.webView; if (webView) { [webView removeFromSuperview]; [contentView addSubview:webView positioned:NSWindowAbove relativeTo:glassView]; } } - + // Configure WebView for liquid glass configureWebViewForLiquidGlass(nsWindow); - + // Make window transparent [window setOpaque:NO]; [window setBackgroundColor:[NSColor clearColor]]; diff --git a/v3/pkg/application/webview_window_windows.go b/v3/pkg/application/webview_window_windows.go index 86a5bb72a10..e8fe1f02b1b 100644 --- a/v3/pkg/application/webview_window_windows.go +++ b/v3/pkg/application/webview_window_windows.go @@ -1802,10 +1802,26 @@ func (w *windowsWebviewWindow) isAlwaysOnTop() bool { // processMessage is given a message sent from JS via the postMessage API // We put it on the global window message buffer to be processed centrally func (w *windowsWebviewWindow) processMessage(message string, sender *edge.ICoreWebView2, args *edge.ICoreWebView2WebMessageReceivedEventArgs) { + topSource, err := sender.GetSource() + if err != nil { + globalApplication.error(fmt.Sprintf("Unable to get source from sender: %s", err.Error())) + topSource = "" + } + + senderSource, err := args.GetSource() + if err != nil { + globalApplication.error(fmt.Sprintf("Unable to get source from args: %s", err.Error())) + senderSource = "" + } + // We send all messages to the centralised window message buffer windowMessageBuffer <- &windowMessage{ windowId: w.parent.id, message: message, + originInfo: &OriginInfo{ + Origin: senderSource, + TopOrigin: topSource, + }, } }