diff --git a/v3/pkg/application/bindings.go b/v3/pkg/application/bindings.go index 7f6e3210..0807ed62 100644 --- a/v3/pkg/application/bindings.go +++ b/v3/pkg/application/bindings.go @@ -308,7 +308,7 @@ func (b *Bindings) getMethods(value interface{}, isPlugin bool) ([]*BoundMethod, var errorType = reflect.TypeFor[error]() // Call will attempt to call this bound method with the given args -func (b *BoundMethod) Call(ctx context.Context, args []json.RawMessage) (returnValue interface{}, err error) { +func (b *BoundMethod) Call(ctx context.Context, window Window, args []json.RawMessage) (returnValue interface{}, err error) { // Use a defer statement to capture panics defer func() { if r := recover(); r != nil { @@ -333,14 +333,8 @@ func (b *BoundMethod) Call(ctx context.Context, args []json.RawMessage) (returnV argCount++ } - if argCount != len(b.Inputs) { - err = fmt.Errorf("%s expects %d arguments, received %d", b.Name, len(b.Inputs), argCount) - return - } - // Convert inputs to values of appropriate type - - callArgs := make([]reflect.Value, argCount) + callArgs := make([]reflect.Value, len(b.Inputs)) base := 0 if b.needsContext { @@ -348,6 +342,23 @@ func (b *BoundMethod) Call(ctx context.Context, args []json.RawMessage) (returnV base++ } + firstArgIsWindow := len(b.Inputs) > 0 && b.Inputs[0].ReflectType == reflect.TypeFor[Window]() + secondArgIsWindow := len(b.Inputs) > 1 && b.Inputs[1].ReflectType == reflect.TypeFor[Window]() + if secondArgIsWindow && !b.needsContext { + return nil, fmt.Errorf("second argument is a Window but first argument is not a context") + } + if firstArgIsWindow || (b.needsContext && secondArgIsWindow) { + // Create a reflect.Value from the window interface + callArgs[base] = reflect.ValueOf(window) + base++ + argCount++ + } + + if argCount != len(b.Inputs) { + err = fmt.Errorf("%s expects %d arguments, received %d", b.Name, len(b.Inputs), argCount) + return + } + // Iterate over given arguments for index, arg := range args { value := reflect.New(b.Inputs[base+index].ReflectType) diff --git a/v3/pkg/application/bindings_test.go b/v3/pkg/application/bindings_test.go index b5ef8db3..f95b4c61 100644 --- a/v3/pkg/application/bindings_test.go +++ b/v3/pkg/application/bindings_test.go @@ -52,6 +52,27 @@ func (t *TestService) Slice(a []int) []int { return a } +func (t *TestService) WithWindow(window application.Window, s string) string { + _ = window + return s +} + +func (t *TestService) WithContext(ctx context.Context, s string) string { + _ = ctx + return s +} + +func (t *TestService) WithContextAndWindow(ctx context.Context, window application.Window, s string) string { + _ = ctx + _ = window + return s +} + +func (t *TestService) WithBadWindow(s string, window application.Window) string { + _ = window + return s +} + func newArgs(jsonArgs ...string) []json.RawMessage { args := []json.RawMessage{} @@ -64,11 +85,12 @@ func newArgs(jsonArgs ...string) []json.RawMessage { func TestBoundMethodCall(t *testing.T) { tests := []struct { - name string - method string - args []json.RawMessage - err error - expected interface{} + name string + method string + args []json.RawMessage + wantWindow bool + err error + expected interface{} }{ { name: "nil", @@ -154,10 +176,41 @@ func TestBoundMethodCall(t *testing.T) { err: nil, expected: []int{1, 2, 3}, }, + { + name: "with window", + method: "WithWindow", + args: newArgs(`"foo"`), + wantWindow: true, + err: nil, + expected: "foo", + }, + { + name: "with context", + method: "WithContext", + args: newArgs(`"foo"`), + err: nil, + expected: "foo", + }, + { + name: "with context and window", + method: "WithContextAndWindow", + args: newArgs(`"foo"`), + wantWindow: true, + err: nil, + expected: "foo", + }, + { + name: "with bad window", + method: "WithBadWindow", + args: newArgs(`"foo"`), + wantWindow: true, + err: errors.New("second argument is a Window but first argument is not a context"), + expected: nil, + }, } // init globalApplication - _ = application.New(application.Options{}) + app := application.New(application.Options{}) bindings, err := application.NewBindings( []any{ @@ -180,8 +233,12 @@ func TestBoundMethodCall(t *testing.T) { if method == nil { t.Fatalf("bound method not found: %s", callOptions.Name()) } + var window application.Window + if tt.wantWindow { + window = app.NewWebviewWindow() + } - result, err := method.Call(context.TODO(), tt.args) + result, err := method.Call(context.TODO(), window, tt.args) if tt.err != err && (tt.err == nil || err == nil || !strings.Contains(err.Error(), tt.err.Error())) { t.Fatalf("error: %v, expected error: %v", err, tt.err) } diff --git a/v3/pkg/application/messageprocessor_call.go b/v3/pkg/application/messageprocessor_call.go index 5cc60e8f..0dab5b92 100644 --- a/v3/pkg/application/messageprocessor_call.go +++ b/v3/pkg/application/messageprocessor_call.go @@ -104,14 +104,7 @@ func (m *MessageProcessor) processCallMethod(method int, rw http.ResponseWriter, m.l.Unlock() }() - // Check if the first bound method parameter is a Window interface - if len(boundMethod.Inputs) > 0 { - if boundMethod.Inputs[0].ReflectType.String() == "application.Window" { - // Prepend the options.Args with the current window - options.Args = append([]interface{}{window}, options.Args...) - } - } - result, err := boundMethod.Call(ctx, options.Args) + result, err := boundMethod.Call(ctx, window, options.Args) if err != nil { m.callErrorCallback(window, "Error calling method: %s", callID, err) return