mirror of
https://github.com/wailsapp/wails.git
synced 2026-03-14 14:45:49 +01:00
[V3] Add origin to raw message handler (#4710)
* Add support for origin tracking in raw message handling - Implemented origin and top origin tracking for web messages from JavaScript. - Updated `RawMessageHandler` to include `originInfo`. - Added cross-platform support for retrieving the origin of messages in macOS, Windows, and Linux. * fix build * fix build * fix build * fix build * fix build * Fix nil checks and string handling for message origins across platforms - Ensure proper fallback to empty strings for `origin` and `topOrigin` when errors or nil values are encountered. - Normalize handling of `message.body` to account for non-NSString values in macOS. * add docs * Remove unused doc * update changelog * fix build --------- Co-authored-by: Lea Anthony <lea.anthony@gmail.com>
This commit is contained in:
parent
9336875969
commit
7d0016bbbe
8 changed files with 122 additions and 49 deletions
|
|
@ -17,6 +17,7 @@ After processing, the content will be moved to the main changelog and this file
|
|||
|
||||
## Added
|
||||
<!-- New features, capabilities, or enhancements -->
|
||||
Add origin to raw message handler by @APshenkin in [PR](https://github.com/wailsapp/wails/pull/4710)
|
||||
Add universal link support for macOS by @APshenkin in [PR](https://github.com/wailsapp/wails/pull/4712)
|
||||
Refactor binding transport layer by @APshenkin in [PR](https://github.com/wailsapp/wails/pull/4702)
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
},
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -237,8 +237,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)
|
||||
|
|
@ -734,7 +741,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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
@ -1645,6 +1657,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)
|
||||
|
|
@ -1653,6 +1676,9 @@ func sendMessageToBackend(contentManager *C.WebKitUserContentManager, result *C.
|
|||
windowMessageBuffer <- &windowMessage{
|
||||
windowId: thisWindowID,
|
||||
message: msg,
|
||||
originInfo: &OriginInfo{
|
||||
Origin: origin,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
#import <QuartzCore/QuartzCore.h>
|
||||
#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 @@ typedef NS_ENUM(NSInteger, MacLiquidGlassStyle) {
|
|||
- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender {
|
||||
NSLog(@"WebviewWindowDelegate: performDragOperation called. WindowID: %u", self.windowId);
|
||||
NSPasteboard *pasteboard = [sender draggingPasteboard];
|
||||
|
||||
|
||||
if (hasListeners(EventWindowFileDraggingPerformed)) {
|
||||
processWindowEvent(self.windowId, EventWindowFileDraggingPerformed);
|
||||
}
|
||||
|
|
@ -295,13 +295,13 @@ typedef NS_ENUM(NSInteger, MacLiquidGlassStyle) {
|
|||
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 @@ typedef NS_ENUM(NSInteger, MacLiquidGlassStyle) {
|
|||
}
|
||||
// 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 @@ typedef NS_ENUM(NSInteger, MacLiquidGlassStyle) {
|
|||
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]];
|
||||
|
|
|
|||
|
|
@ -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("Unable to get source from sender: %s", err.Error())
|
||||
topSource = ""
|
||||
}
|
||||
|
||||
senderSource, err := args.GetSource()
|
||||
if err != nil {
|
||||
globalApplication.error("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,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue