diff --git a/v3/examples/contextmenus/main.go b/v3/examples/contextmenus/main.go index 0ed4fcc69..a94b9d340 100644 --- a/v3/examples/contextmenus/main.go +++ b/v3/examples/contextmenus/main.go @@ -20,13 +20,13 @@ func main() { Mac: application.MacOptions{ ApplicationShouldTerminateAfterLastWindowClosed: true, }, + Assets: application.AssetOptions{ + FS: assets, + }, }) mainWindow := app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ Title: "Context Menu Demo", - Assets: application.AssetOptions{ - FS: assets, - }, Mac: application.MacWindow{ Backdrop: application.MacBackdropTranslucent, TitleBar: application.MacTitleBarHiddenInsetUnified, @@ -36,9 +36,6 @@ func main() { app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ Title: "Context Menu Demo", - Assets: application.AssetOptions{ - FS: assets, - }, Mac: application.MacWindow{ Backdrop: application.MacBackdropTranslucent, TitleBar: application.MacTitleBarHiddenInsetUnified, diff --git a/v3/examples/drag-n-drop/main.go b/v3/examples/drag-n-drop/main.go index 8a7974c26..fe781c39d 100644 --- a/v3/examples/drag-n-drop/main.go +++ b/v3/examples/drag-n-drop/main.go @@ -20,13 +20,13 @@ func main() { Mac: application.MacOptions{ ApplicationShouldTerminateAfterLastWindowClosed: true, }, + Assets: application.AssetOptions{ + FS: assets, + }, }) window := app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ Title: "Drag-n-drop Demo", - Assets: application.AssetOptions{ - FS: assets, - }, Mac: application.MacWindow{ Backdrop: application.MacBackdropTranslucent, TitleBar: application.MacTitleBarHiddenInsetUnified, diff --git a/v3/examples/events/main.go b/v3/examples/events/main.go index 241602785..68a0a8ef0 100644 --- a/v3/examples/events/main.go +++ b/v3/examples/events/main.go @@ -21,6 +21,9 @@ func main() { Mac: application.MacOptions{ ApplicationShouldTerminateAfterLastWindowClosed: true, }, + Assets: application.AssetOptions{ + FS: assets, + }, }) app.Events.On("myevent", func(e *application.CustomEvent) { @@ -40,9 +43,6 @@ func main() { app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ Title: "Events Demo", - Assets: application.AssetOptions{ - FS: assets, - }, Mac: application.MacWindow{ Backdrop: application.MacBackdropTranslucent, TitleBar: application.MacTitleBarHiddenInsetUnified, @@ -51,9 +51,6 @@ func main() { }) app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ Title: "Events Demo", - Assets: application.AssetOptions{ - FS: assets, - }, Mac: application.MacWindow{ Backdrop: application.MacBackdropTranslucent, TitleBar: application.MacTitleBarHiddenInsetUnified, diff --git a/v3/examples/plain/main.go b/v3/examples/plain/main.go index 9a965e485..abbc8a471 100644 --- a/v3/examples/plain/main.go +++ b/v3/examples/plain/main.go @@ -15,9 +15,15 @@ func main() { Mac: application.MacOptions{ ApplicationShouldTerminateAfterLastWindowClosed: true, }, + Assets: application.AssetOptions{ + Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Write([]byte(`Plain Bundle

Plain Bundle

This is a plain bundle. It has no frontend code but this was Served by the AssetServer's Handler.



Clicking this paragraph emits an event...

`)) + }), + }, }) // Create window - myWindow := app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ + app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ Title: "Plain Bundle", CSS: `body { background-color: rgba(255, 255, 255, 0); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; user-select: none; -ms-user-select: none; -webkit-user-select: none; } .main { color: white; margin: 20%; }`, Mac: application.MacWindow{ @@ -26,12 +32,6 @@ func main() { TitleBar: application.MacTitleBarHiddenInsetUnified, }, URL: "/", - Assets: application.AssetOptions{ - Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - w.Write([]byte(`Plain Bundle

Plain Bundle

This is a plain bundle. It has no frontend code but this was Served by the AssetServer's Handler.



Clicking this paragraph emits an event...

`)) - }), - }, }) app.Events.On("clicked", func(_ *application.CustomEvent) { diff --git a/v3/examples/screen/main.go b/v3/examples/screen/main.go index 8225d68fc..e71f5c4be 100644 --- a/v3/examples/screen/main.go +++ b/v3/examples/screen/main.go @@ -19,15 +19,15 @@ func main() { Mac: application.MacOptions{ ApplicationShouldTerminateAfterLastWindowClosed: true, }, + Assets: application.AssetOptions{ + FS: assets, + }, }) app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ Title: "Screen Demo", Width: 800, Height: 600, - Assets: application.AssetOptions{ - FS: assets, - }, Mac: application.MacWindow{ Backdrop: application.MacBackdropTranslucent, TitleBar: application.MacTitleBarHiddenInsetUnified, diff --git a/v3/examples/windowjs/main.go b/v3/examples/windowjs/main.go index 0ce071f1a..3fb919bf0 100644 --- a/v3/examples/windowjs/main.go +++ b/v3/examples/windowjs/main.go @@ -21,6 +21,9 @@ func main() { Mac: application.MacOptions{ ApplicationShouldTerminateAfterLastWindowClosed: true, }, + Assets: application.AssetOptions{ + FS: assets, + }, }) // Create a custom menu @@ -30,11 +33,7 @@ func main() { windowCounter := 1 newWindow := func() { - app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ - Assets: application.AssetOptions{ - FS: assets, - }, - }). + app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{}). SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)). SetPosition(rand.Intn(1000), rand.Intn(800)). Show() diff --git a/v3/examples/wml/main.go b/v3/examples/wml/main.go index a62385119..765eb2001 100644 --- a/v3/examples/wml/main.go +++ b/v3/examples/wml/main.go @@ -19,15 +19,15 @@ func main() { Mac: application.MacOptions{ ApplicationShouldTerminateAfterLastWindowClosed: true, }, + Assets: application.AssetOptions{ + FS: assets, + }, }) app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ Title: "Wails ML Demo", Width: 800, Height: 600, - Assets: application.AssetOptions{ - FS: assets, - }, Mac: application.MacWindow{ Backdrop: application.MacBackdropTranslucent, TitleBar: application.MacTitleBarHiddenInsetUnified, diff --git a/v3/pkg/application/application.go b/v3/pkg/application/application.go index bce262d81..1ff4ad5a9 100644 --- a/v3/pkg/application/application.go +++ b/v3/pkg/application/application.go @@ -3,14 +3,19 @@ package application import "C" import ( "log" + "net/http" "os" "runtime" + "strconv" "sync" - "github.com/wailsapp/wails/v3/pkg/logger" - + "github.com/wailsapp/wails/v2/pkg/assetserver" "github.com/wailsapp/wails/v2/pkg/assetserver/webview" + assetserveroptions "github.com/wailsapp/wails/v2/pkg/options/assetserver" + + wailsruntime "github.com/wailsapp/wails/v3/internal/runtime" "github.com/wailsapp/wails/v3/pkg/events" + "github.com/wailsapp/wails/v3/pkg/logger" ) var globalApplication *App @@ -39,6 +44,17 @@ func New(appOptions Options) *App { } result.Events = NewCustomEventProcessor(result.dispatchEventToWindows) + + opts := assetserveroptions.Options{Assets: appOptions.Assets.FS, Handler: appOptions.Assets.Handler, Middleware: appOptions.Assets.Middleware} + // TODO ServingFrom disk? + srv, err := assetserver.NewAssetServer("", opts, false, nil, wailsruntime.RuntimeAssetsBundle) + if err != nil { + result.fatal(err.Error()) + } + + srv.UseRuntimeHandler(NewMessageProcessor()) + result.assets = srv + globalApplication = result return result } @@ -85,9 +101,24 @@ type dragAndDropMessage struct { var windowDragAndDropBuffer = make(chan *dragAndDropMessage) +var _ webview.Request = &webViewAssetRequest{} + +const webViewRequestHeaderWindowId = "x-wails-window-id" + type webViewAssetRequest struct { + webview.Request windowId uint - request webview.Request +} + +func (r *webViewAssetRequest) Header() (http.Header, error) { + h, err := r.Request.Header() + if err != nil { + return nil, err + } + + hh := h.Clone() + hh.Set(webViewRequestHeaderWindowId, strconv.FormatUint(uint64(r.windowId), 10)) + return hh, nil } var webviewRequests = make(chan *webViewAssetRequest) @@ -127,6 +158,8 @@ type App struct { contextMenus map[string]*Menu contextMenusLock sync.Mutex + + assets *assetserver.AssetServer } func (a *App) getSystemTrayID() uint { @@ -243,9 +276,9 @@ func (a *App) Run() error { }() go func() { for { - event := <-webviewRequests - a.handleWebViewRequest(event) - err := event.request.Release() + request := <-webviewRequests + a.handleWebViewRequest(request) + err := request.Release() if err != nil { a.error("Failed to release webview request: %s", err.Error()) } @@ -334,17 +367,11 @@ func (a *App) handleWindowMessage(event *windowMessage) { window.handleMessage(event.message) } -func (a *App) handleWebViewRequest(event *webViewAssetRequest) { +func (a *App) handleWebViewRequest(request *webViewAssetRequest) { // Get window from window map - a.windowsLock.Lock() - window, ok := a.windows[event.windowId] - a.windowsLock.Unlock() - if !ok { - log.Printf("WebviewWindow #%d not found", event.windowId) - return - } - // Get callback from window - window.handleWebViewRequest(event.request) + url, _ := request.URL() + a.info("Window: %d, Request: %s", request.windowId, url) + a.assets.ServeWebViewRequest(request) } func (a *App) handleWindowEvent(event *WindowEvent) { diff --git a/v3/pkg/application/application_darwin.go b/v3/pkg/application/application_darwin.go index 12b9c4300..6a1124f12 100644 --- a/v3/pkg/application/application_darwin.go +++ b/v3/pkg/application/application_darwin.go @@ -225,8 +225,8 @@ func processMessage(windowID C.uint, message *C.char) { //export processURLRequest func processURLRequest(windowID C.uint, wkUrlSchemeTask unsafe.Pointer) { webviewRequests <- &webViewAssetRequest{ + Request: webview.NewRequest(wkUrlSchemeTask), windowId: uint(windowID), - request: webview.NewRequest(wkUrlSchemeTask), } } diff --git a/v3/pkg/application/messageprocessor.go b/v3/pkg/application/messageprocessor.go index 3a7fdeb9c..08bffa429 100644 --- a/v3/pkg/application/messageprocessor.go +++ b/v3/pkg/application/messageprocessor.go @@ -3,19 +3,19 @@ package application import ( "fmt" "net/http" + "strconv" "strings" jsoniter "github.com/json-iterator/go" ) -type MessageProcessor struct { - window *WebviewWindow -} +// TODO maybe we could use a new struct that has the targetWindow as an attribute so we could get rid of passing the targetWindow +// as parameter through every function call. -func NewMessageProcessor(w *WebviewWindow) *MessageProcessor { - return &MessageProcessor{ - window: w, - } +type MessageProcessor struct{} + +func NewMessageProcessor() *MessageProcessor { + return &MessageProcessor{} } func (m *MessageProcessor) httpError(rw http.ResponseWriter, message string, args ...any) { @@ -43,15 +43,27 @@ func (m *MessageProcessor) HandleRuntimeCall(rw http.ResponseWriter, r *http.Req params := QueryParams(r.URL.Query()) - var targetWindow = m.window - windowID := params.UInt("windowID") - if windowID != nil { - // Get window for ID - targetWindow = globalApplication.getWindowForID(*windowID) - if targetWindow == nil { - m.Error("Window ID %s not found", *windowID) + var windowID uint + if hWindowID := r.Header.Get(webViewRequestHeaderWindowId); hWindowID != "" { + // Get windowID out of the request header + wID, err := strconv.ParseUint(hWindowID, 10, 64) + if err != nil { + m.Error("Window ID '%s' not parsable: %s", hWindowID, err) return } + + windowID = uint(wID) + } + + if qWindowID := params.UInt("windowID"); qWindowID != nil { + // Get windowID out of the query parameters if provided + windowID = *qWindowID + } + + targetWindow := globalApplication.getWindowForID(windowID) + if targetWindow == nil { + m.Error("Window ID %s not found", windowID) + return } switch object { @@ -79,10 +91,6 @@ func (m *MessageProcessor) HandleRuntimeCall(rw http.ResponseWriter, r *http.Req } -func (m *MessageProcessor) ProcessMessage(message string) { - m.Info("ProcessMessage from front end:", message) -} - func (m *MessageProcessor) Error(message string, args ...any) { fmt.Printf("[MessageProcessor] Error: "+message, args...) } diff --git a/v3/pkg/application/messageprocessor_call.go b/v3/pkg/application/messageprocessor_call.go index 49bb78263..ddbddd37f 100644 --- a/v3/pkg/application/messageprocessor_call.go +++ b/v3/pkg/application/messageprocessor_call.go @@ -7,19 +7,19 @@ import ( "strconv" ) -func (m *MessageProcessor) callErrorCallback(message string, callID *string, err error) { +func (m *MessageProcessor) callErrorCallback(window *WebviewWindow, message string, callID *string, err error) { errorMsg := fmt.Sprintf(message, err) m.Error(errorMsg) msg := "_wails.callErrorCallback('" + *callID + "', " + strconv.Quote(errorMsg) + ");" - m.window.ExecJS(msg) + window.ExecJS(msg) } -func (m *MessageProcessor) callCallback(callID *string, result string, isJSON bool) { +func (m *MessageProcessor) callCallback(window *WebviewWindow, callID *string, result string, isJSON bool) { msg := fmt.Sprintf("_wails.callCallback('%s', %s, %v);", *callID, strconv.Quote(result), isJSON) - m.window.ExecJS(msg) + window.ExecJS(msg) } -func (m *MessageProcessor) processCallMethod(method string, rw http.ResponseWriter, _ *http.Request, _ *WebviewWindow, params QueryParams) { +func (m *MessageProcessor) processCallMethod(method string, rw http.ResponseWriter, _ *http.Request, window *WebviewWindow, params QueryParams) { args, err := params.Args() if err != nil { m.httpError(rw, "Unable to parse arguments: %s", err) @@ -35,27 +35,27 @@ func (m *MessageProcessor) processCallMethod(method string, rw http.ResponseWrit var options CallOptions err := params.ToStruct(&options) if err != nil { - m.callErrorCallback("Error parsing call options: %s", callID, err) + m.callErrorCallback(window, "Error parsing call options: %s", callID, err) return } bindings := globalApplication.bindings.Get(&options) if bindings == nil { - m.callErrorCallback("Error getting binding for method: %s", callID, fmt.Errorf("'%s' not found", options.MethodName)) + m.callErrorCallback(window, "Error getting binding for method: %s", callID, fmt.Errorf("'%s' not found", options.MethodName)) return } go func() { result, err := bindings.Call(options.Args) if err != nil { - m.callErrorCallback("Error calling method: %s", callID, err) + m.callErrorCallback(window, "Error calling method: %s", callID, err) return } // convert result to json jsonResult, err := json.Marshal(result) if err != nil { - m.callErrorCallback("Error converting result to json: %s", callID, err) + m.callErrorCallback(window, "Error converting result to json: %s", callID, err) return } - m.callCallback(callID, string(jsonResult), true) + m.callCallback(window, callID, string(jsonResult), true) }() m.ok(rw) default: diff --git a/v3/pkg/application/messageprocessor_dialog.go b/v3/pkg/application/messageprocessor_dialog.go index ce5bebeaf..84f519a43 100644 --- a/v3/pkg/application/messageprocessor_dialog.go +++ b/v3/pkg/application/messageprocessor_dialog.go @@ -8,16 +8,16 @@ import ( "strconv" ) -func (m *MessageProcessor) dialogErrorCallback(message string, dialogID *string, err error) { +func (m *MessageProcessor) dialogErrorCallback(window *WebviewWindow, message string, dialogID *string, err error) { errorMsg := fmt.Sprintf(message, err) m.Error(errorMsg) msg := "_wails.dialogErrorCallback('" + *dialogID + "', " + strconv.Quote(errorMsg) + ");" - m.window.ExecJS(msg) + window.ExecJS(msg) } -func (m *MessageProcessor) dialogCallback(dialogID *string, result string, isJSON bool) { +func (m *MessageProcessor) dialogCallback(window *WebviewWindow, dialogID *string, result string, isJSON bool) { msg := fmt.Sprintf("_wails.dialogCallback('%s', %s, %v);", *dialogID, strconv.Quote(result), isJSON) - m.window.ExecJS(msg) + window.ExecJS(msg) } func (m *MessageProcessor) processDialogMethod(method string, rw http.ResponseWriter, r *http.Request, window *WebviewWindow, params QueryParams) { @@ -37,7 +37,7 @@ func (m *MessageProcessor) processDialogMethod(method string, rw http.ResponseWr var options MessageDialogOptions err := params.ToStruct(&options) if err != nil { - m.dialogErrorCallback("Error parsing dialog options: %s", dialogID, err) + m.dialogErrorCallback(window, "Error parsing dialog options: %s", dialogID, err) return } if len(options.Buttons) == 0 { @@ -63,7 +63,7 @@ func (m *MessageProcessor) processDialogMethod(method string, rw http.ResponseWr for _, button := range options.Buttons { label := button.Label button.OnClick(func() { - m.dialogCallback(dialogID, label, false) + m.dialogCallback(window, dialogID, label, false) }) } dialog.AddButtons(options.Buttons) @@ -82,23 +82,23 @@ func (m *MessageProcessor) processDialogMethod(method string, rw http.ResponseWr if options.AllowsMultipleSelection { files, err := dialog.PromptForMultipleSelection() if err != nil { - m.dialogErrorCallback("Error getting selection: %s", dialogID, err) + m.dialogErrorCallback(window, "Error getting selection: %s", dialogID, err) return } else { result, err := json.Marshal(files) if err != nil { - m.dialogErrorCallback("Error marshalling files: %s", dialogID, err) + m.dialogErrorCallback(window, "Error marshalling files: %s", dialogID, err) return } - m.dialogCallback(dialogID, string(result), true) + m.dialogCallback(window, dialogID, string(result), true) } } else { file, err := dialog.PromptForSingleSelection() if err != nil { - m.dialogErrorCallback("Error getting selection: %s", dialogID, err) + m.dialogErrorCallback(window, "Error getting selection: %s", dialogID, err) return } - m.dialogCallback(dialogID, file, false) + m.dialogCallback(window, dialogID, file, false) } }() m.ok(rw) @@ -114,10 +114,10 @@ func (m *MessageProcessor) processDialogMethod(method string, rw http.ResponseWr go func() { file, err := dialog.PromptForSingleSelection() if err != nil { - m.dialogErrorCallback("Error getting selection: %s", dialogID, err) + m.dialogErrorCallback(window, "Error getting selection: %s", dialogID, err) return } - m.dialogCallback(dialogID, file, false) + m.dialogCallback(window, dialogID, file, false) }() m.ok(rw) diff --git a/v3/pkg/application/options_application.go b/v3/pkg/application/options_application.go index cfb11ee76..69ede8ab9 100644 --- a/v3/pkg/application/options_application.go +++ b/v3/pkg/application/options_application.go @@ -1,6 +1,9 @@ package application import ( + "io/fs" + "net/http" + "github.com/wailsapp/wails/v3/pkg/logger" ) @@ -14,4 +17,14 @@ type Options struct { Silent bool CustomLoggers []logger.Output } + Assets AssetOptions +} + +type AssetOptions struct { + // FS to use for loading assets from + FS fs.FS + // Handler is a custom handler to use for serving assets. If this is set, the `URL` and `FS` fields are ignored. + Handler http.Handler + // Middleware is a custom middleware to use for serving assets. If this is set, the `URL` and `FS` fields are ignored. + Middleware func(http.Handler) http.Handler } diff --git a/v3/pkg/application/options_webview_window.go b/v3/pkg/application/options_webview_window.go index c8e748beb..5e5e9647a 100644 --- a/v3/pkg/application/options_webview_window.go +++ b/v3/pkg/application/options_webview_window.go @@ -1,10 +1,5 @@ package application -import ( - "io/fs" - "net/http" -) - type WindowState int const ( @@ -29,7 +24,6 @@ type WebviewWindowOptions struct { StartState WindowState Mac MacWindow BackgroundColour *RGBA - Assets AssetOptions HTML string JS string CSS string @@ -49,14 +43,6 @@ var WebviewWindowDefaults = &WebviewWindowOptions{ URL: "", } -type AssetOptions struct { - // FS to use for loading assets from - FS fs.FS - // Handler is a custom handler to use for serving assets. If this is set, the `URL` and `FS` fields are ignored. - Handler http.Handler - // Middleware is a custom middleware to use for serving assets. If this is set, the `URL` and `FS` fields are ignored. - Middleware func(http.Handler) http.Handler -} type RGBA struct { Red, Green, Blue, Alpha uint8 diff --git a/v3/pkg/application/webview_window.go b/v3/pkg/application/webview_window.go index dc2a7ddbb..d5ab8a8a9 100644 --- a/v3/pkg/application/webview_window.go +++ b/v3/pkg/application/webview_window.go @@ -7,10 +7,6 @@ import ( "github.com/wailsapp/wails/v3/pkg/logger" - "github.com/wailsapp/wails/v2/pkg/assetserver" - "github.com/wailsapp/wails/v2/pkg/assetserver/webview" - assetserveroptions "github.com/wailsapp/wails/v2/pkg/options/assetserver" - "github.com/wailsapp/wails/v3/internal/runtime" "github.com/wailsapp/wails/v3/pkg/events" ) @@ -72,9 +68,6 @@ type WebviewWindow struct { implLock sync.RWMutex id uint - assets *assetserver.AssetServer - messageProcessor *MessageProcessor - eventListeners map[uint][]func(ctx *WindowEventContext) eventListenersLock sync.RWMutex @@ -103,25 +96,13 @@ func NewWindow(options *WebviewWindowOptions) *WebviewWindow { options.URL = "/" } - opts := assetserveroptions.Options{Assets: options.Assets.FS, Handler: options.Assets.Handler, Middleware: options.Assets.Middleware} - // TODO Bindings, ServingFrom disk? - srv, err := assetserver.NewAssetServer("", opts, false, nil, runtime.RuntimeAssetsBundle) - if err != nil { - globalApplication.fatal(err.Error()) - } - result := &WebviewWindow{ id: getWindowID(), options: options, eventListeners: make(map[uint][]func(ctx *WindowEventContext)), contextMenus: make(map[string]*Menu), - - assets: srv, } - result.messageProcessor = NewMessageProcessor(result) - srv.UseRuntimeHandler(result.messageProcessor) - return result } @@ -383,16 +364,10 @@ func (w *WebviewWindow) handleMessage(message string) { if message == "test" { w.SetTitle("Hello World") } - w.messageProcessor.ProcessMessage(message) + w.info("ProcessMessage from front end:", message) } -func (w *WebviewWindow) handleWebViewRequest(request webview.Request) { - url, _ := request.URL() - w.info("Request: %s", url) - w.assets.ServeWebViewRequest(request) -} - func (w *WebviewWindow) Center() { if w.impl == nil { return diff --git a/v3/pkg/application/webview_window_darwin.go b/v3/pkg/application/webview_window_darwin.go index 2484d2460..6a51f6849 100644 --- a/v3/pkg/application/webview_window_darwin.go +++ b/v3/pkg/application/webview_window_darwin.go @@ -790,6 +790,7 @@ static void windowSetFrameless(void *window, bool frameless) { */ import "C" import ( + "net/url" "sync" "unsafe" @@ -969,13 +970,17 @@ func (w *macosWebviewWindow) execJS(js string) { C.windowExecJS(w.nsWindow, C.CString(js)) } -func (w *macosWebviewWindow) setURL(url string) { - if url == "/" { - // TODO handle this in a central location and handle all urls without scheme and host. This might be platform - // dependant - url = "wails://wails/" +func (w *macosWebviewWindow) setURL(uri string) { + if uri != "" { + url, err := url.Parse(uri) + if err == nil && url.Scheme == "" && url.Host == "" { + // TODO handle this in a central location, the scheme and host might be platform dependant. + url.Scheme = "wails" + url.Host = "wails" + uri = url.String() + } } - C.navigationLoadURL(w.nsWindow, C.CString(url)) + C.navigationLoadURL(w.nsWindow, C.CString(uri)) } func (w *macosWebviewWindow) setAlwaysOnTop(alwaysOnTop bool) {