public async Task BeginInvokeDotNetFromJS(string callId, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson)
        {
            try
            {
                AssertInitialized();
                if (assemblyName == "Microsoft.AspNetCore.Components.Web" && methodIdentifier == "DispatchEvent")
                {
                    Log.DispatchEventTroughJSInterop(_logger);
                    return;
                }

                await Renderer.Dispatcher.InvokeAsync(() =>
                {
                    SetCurrentCircuitHost(this);
                    Log.BeginInvokeDotNet(_logger, callId, assemblyName, methodIdentifier, dotNetObjectId);
                    DotNetDispatcher.BeginInvoke(callId, assemblyName, methodIdentifier, dotNetObjectId, argsJson);
                });
            }
            catch (Exception ex)
            {
                // We don't expect any of this code to actually throw, because DotNetDispatcher.BeginInvoke doesn't throw
                // however, we still want this to get logged if we do.
                UnhandledException?.Invoke(this, new UnhandledExceptionEventArgs(ex, isTerminating: false));
            }
        }
        public async Task SetCertificateAsync_should_get_certificate_thumbprint()
        {
            string relyingPartyId = await CreateEntity(null);

            CreateTestHost("Alice Smith",
                           SharedConstants.WRITER,
                           relyingPartyId,
                           out TestHost host,
                           out RenderedComponent <App> component,
                           out MockHttpMessageHandler _,
                           true);

            var jsRuntime = host.ServiceProvider.GetRequiredService <JSRuntimeImpl>();

            WaitForLoaded(host, component);

            WaitForContains(host, component, "filtered");

            jsRuntime.Called.WaitOne();

            await Task.Delay(100).ConfigureAwait(false);

            DotNetDispatcher.BeginInvokeDotNet(jsRuntime, new DotNetInvocationInfo(null, "NotifyChange", 1, default), "[[{ \"name\": \"test.crt\" }]]");

            host.WaitForNextRender();

            string markup = component.GetMarkup();

            Assert.Contains("Invalid file", markup);
        }
Exemple #3
0
        // BeginInvokeDotNetFromJS is used in a fire-and-forget context, so it's responsible for its own
        // error handling.
        public async Task BeginInvokeDotNetFromJS(string callId, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson)
        {
            AssertInitialized();
            AssertNotDisposed();

            try
            {
                await Renderer.Dispatcher.InvokeAsync(() =>
                {
                    SetCurrentCircuitHost(this);
                    Log.BeginInvokeDotNet(_logger, callId, assemblyName, methodIdentifier, dotNetObjectId);
                    DotNetDispatcher.BeginInvoke(callId, assemblyName, methodIdentifier, dotNetObjectId, argsJson);
                });
            }
            catch (Exception ex)
            {
                // We don't expect any of this code to actually throw, because DotNetDispatcher.BeginInvoke doesn't throw
                // however, we still want this to get logged if we do.
                Log.BeginInvokeDotNetFailed(_logger, callId, assemblyName, methodIdentifier, dotNetObjectId, ex);
                if (Client.Connected)
                {
                    await NotifyClientError(Client, "Interop call failed.");
                }
                UnhandledException?.Invoke(this, new UnhandledExceptionEventArgs(ex, isTerminating: false));
            }
        }
        // EndInvokeJSFromDotNet is used in a fire-and-forget context, so it's responsible for its own
        // error handling.
        public async Task EndInvokeJSFromDotNet(long asyncCall, bool succeeded, string arguments)
        {
            AssertInitialized();
            AssertNotDisposed();

            try
            {
                await Renderer.Dispatcher.InvokeAsync(() =>
                {
                    if (!succeeded)
                    {
                        // We can log the arguments here because it is simply the JS error with the call stack.
                        Log.EndInvokeJSFailed(_logger, asyncCall, arguments);
                    }
                    else
                    {
                        Log.EndInvokeJSSucceeded(_logger, asyncCall);
                    }

                    DotNetDispatcher.EndInvokeJS(JSRuntime, arguments);
                });
            }
            catch (Exception ex)
            {
                // An error completing JS interop means that the user sent invalid data, a well-behaved
                // client won't do this.
                Log.EndInvokeDispatchException(_logger, ex);
                await TryNotifyClientErrorAsync(Client, GetClientErrorMessage(ex, "Invalid interop arguments."));

                UnhandledException?.Invoke(this, new UnhandledExceptionEventArgs(ex, isTerminating: false));
            }
        }
Exemple #5
0
 private static void BeginInvokeDotNet(PageContext pageContext, string callId, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson)
 {
     DotNetDispatcher.BeginInvokeDotNet(
         pageContext.JSRuntime,
         new DotNetInvocationInfo(assemblyName, methodIdentifier, dotNetObjectId, callId),
         argsJson);
 }
        public async Task EndInvokeJSFromDotNet(long asyncCall, bool succeded, string arguments)
        {
            try
            {
                AssertInitialized();

                await Renderer.Dispatcher.InvokeAsync(() =>
                {
                    SetCurrentCircuitHost(this);
                    if (!succeded)
                    {
                        // We can log the arguments here because it is simply the JS error with the call stack.
                        Log.EndInvokeJSFailed(_logger, asyncCall, arguments);
                    }
                    else
                    {
                        Log.EndInvokeJSSucceeded(_logger, asyncCall);
                    }

                    DotNetDispatcher.EndInvoke(arguments);
                });
            }
            catch (Exception ex)
            {
                Log.EndInvokeDispatchException(_logger, ex);
            }
        }
Exemple #7
0
        private static void AttachJsInterop(IPC ipc, CancellationToken appLifetime)
        {
            var desktopSynchronizationContext = new DesktopSynchronizationContext(appLifetime);

            SynchronizationContext.SetSynchronizationContext(desktopSynchronizationContext);

            ipc.On("BeginInvokeDotNetFromJS", args =>
            {
                desktopSynchronizationContext.Send(state =>
                {
                    var argsArray = (object[])state;
                    DotNetDispatcher.BeginInvokeDotNet(
                        DesktopJSRuntime,
                        new DotNetInvocationInfo(
                            assemblyName: ((JsonElement)argsArray[1]).GetString(),
                            methodIdentifier: ((JsonElement)argsArray[2]).GetString(),
                            dotNetObjectId: ((JsonElement)argsArray[3]).GetInt64(),
                            callId: ((JsonElement)argsArray[0]).GetString()),
                        ((JsonElement)argsArray[4]).GetString());
                }, args);
            });

            ipc.On("EndInvokeJSFromDotNet", args =>
            {
                desktopSynchronizationContext.Send(state =>
                {
                    var argsArray = (object[])state;
                    DotNetDispatcher.EndInvokeJS(
                        DesktopJSRuntime,
                        ((JsonElement)argsArray[2]).GetString());
                }, args);
            });
        }
Exemple #8
0
        private Task <InteropHandshakeResult> AttachInteropAsync()
        {
            var resultTcs = new TaskCompletionSource <InteropHandshakeResult>();

            // These hacks can go away once there's a proper IPC channel for event notifications etc.
            var selfAsDotNetObjectReference = typeof(DotNetObjectReference).GetMethod(nameof(DotNetObjectReference.Create))
                                              .MakeGenericMethod(GetType())
                                              .Invoke(null, new[] { this });
            var selfAsDotnetObjectReferenceId = (long)typeof(JSRuntime).GetMethod("TrackObjectReference", BindingFlags.NonPublic | BindingFlags.Instance)
                                                .MakeGenericMethod(GetType())
                                                .Invoke(_jsRuntime, new[] { selfAsDotNetObjectReference });

            _ipc.Once("components:init", args =>
            {
                var argsArray          = (object[])args;
                var initialUriAbsolute = ((JsonElement)argsArray[0]).GetString();
                var baseUriAbsolute    = ((JsonElement)argsArray[1]).GetString();
                resultTcs.TrySetResult(new InteropHandshakeResult(baseUriAbsolute, initialUriAbsolute));
            });

            _ipc.On("BeginInvokeDotNetFromJS", args =>
            {
                var argsArray        = (object[])args;
                var assemblyName     = argsArray[1] != null ? ((JsonElement)argsArray[1]).GetString() : null;
                var methodIdentifier = ((JsonElement)argsArray[2]).GetString();
                var dotNetObjectId   = ((JsonElement)argsArray[3]).GetInt64();
                var callId           = ((JsonElement)argsArray[0]).GetString();
                var argsJson         = ((JsonElement)argsArray[4]).GetString();

                // As a temporary hack, intercept blazor.desktop.js's JS interop calls for event notifications,
                // and direct them to our own instance. This is to avoid needing a static BlazorHybridRenderer.Instance.
                // Similar temporary hack for navigation notifications
                // TODO: Change blazor.desktop.js to use a dedicated IPC call for these calls, not JS interop.
                if (assemblyName == "Microsoft.MobileBlazorBindings.WebView")
                {
                    assemblyName   = null;
                    dotNetObjectId = selfAsDotnetObjectReferenceId;
                }

                DotNetDispatcher.BeginInvokeDotNet(
                    _jsRuntime,
                    new DotNetInvocationInfo(assemblyName, methodIdentifier, dotNetObjectId, callId),
                    argsJson);
            });

            _ipc.On("EndInvokeJSFromDotNet", args =>
            {
                var argsArray = (object[])args;
                DotNetDispatcher.EndInvokeJS(
                    _jsRuntime,
                    ((JsonElement)argsArray[2]).GetString());
            });

            _webView.Source = new XF.UrlWebViewSource {
                Url = $"{BlazorAppScheme}://0.0.0.0/"
            };

            return(resultTcs.Task);
        }
 // Invoked via Mono's JS interop mechanism (invoke_method)
 public static void EndInvokeJS(string argsJson)
 {
     WebAssemblyCallQueue.Schedule(argsJson, static argsJson =>
     {
         // This is not expected to throw, as it takes care of converting any unhandled user code
         // exceptions into a failure on the Task that was returned when calling InvokeAsync.
         DotNetDispatcher.EndInvokeJS(Instance, argsJson);
     });
 }
Exemple #10
0
        [InlineData("MethodWithoutAttribute")] // That's not really its identifier; just making the point that there's no way to invoke it
        public void CannotInvokeUnsuitableMethods(string methodIdentifier)
        {
            var ex = Assert.Throws <ArgumentException>(() =>
            {
                DotNetDispatcher.Invoke(thisAssemblyName, methodIdentifier, null);
            });

            Assert.Equal($"The assembly '{thisAssemblyName}' does not contain a public method with [JSInvokableAttribute(\"{methodIdentifier}\")].", ex.Message);
        }
Exemple #11
0
        public void CanInvokeStaticNonVoidMethod()
        {
            // Arrange/Act
            var resultJson = DotNetDispatcher.Invoke(thisAssemblyName, "InvocableStaticNonVoid", null);
            var result     = Json.Deserialize <TestDTO>(resultJson);

            // Assert
            Assert.Equal("Test", result.StringVal);
            Assert.Equal(123, result.IntVal);
        }
Exemple #12
0
        public void CanInvokeStaticVoidMethod()
        {
            // Arrange/Act
            SomePublicType.DidInvokeMyInvocableVoid = false;
            var resultJson = DotNetDispatcher.Invoke(thisAssemblyName, "InvocableStaticVoid", null);

            // Assert
            Assert.Null(resultJson);
            Assert.True(SomePublicType.DidInvokeMyInvocableVoid);
        }
Exemple #13
0
        public void CannotInvokeMethodsOnUnloadedAssembly()
        {
            var assemblyName = "Some.Fake.Assembly";
            var ex           = Assert.Throws <ArgumentException>(() =>
            {
                DotNetDispatcher.Invoke(assemblyName, "SomeMethod", null);
            });

            Assert.Equal($"There is no loaded assembly with the name '{assemblyName}'.", ex.Message);
        }
Exemple #14
0
        public void CannotInvokeWithEmptyMethodIdentifier()
        {
            var ex = Assert.Throws <ArgumentException>(() =>
            {
                DotNetDispatcher.Invoke("SomeAssembly", " ", "[]");
            });

            Assert.StartsWith("Cannot be null, empty, or whitespace.", ex.Message);
            Assert.Equal("methodIdentifier", ex.ParamName);
        }
Exemple #15
0
 private void EndInvokeJS(PageContext pageContext, long asyncHandle, bool succeeded, string argumentsOrError)
 {
     if (succeeded)
     {
         DotNetDispatcher.EndInvokeJS(pageContext.JSRuntime, argumentsOrError);
     }
     else
     {
         throw new InvalidOperationException(argumentsOrError);
     }
 }
Exemple #16
0
        public void CannotInvokeWithIncorrectNumberOfParams()
        {
            // Arrange
            var argsJson = Json.Serialize(new object[] { 1, 2, 3, 4 });

            // Act/Assert
            var ex = Assert.Throws <ArgumentException>(() =>
            {
                DotNetDispatcher.Invoke(thisAssemblyName, "InvocableStaticWithParams", argsJson);
            });

            Assert.Equal("In call to 'InvocableStaticWithParams', expected 2 parameters but received 4.", ex.Message);
        }
Exemple #17
0
        public void HandleFileSelected_should_report_importation_result()
        {
            CreateTestHost("Alice Smith",
                           SharedConstants.WRITER,
                           out RenderedComponent <App> component);

            WaitForLoaded(component);

            var jsRuntime = _host.ServiceProvider.GetRequiredService <JSRuntimeImpl>();

            DotNetDispatcher.BeginInvokeDotNet(jsRuntime, new DotNetInvocationInfo(null, nameof(InputFile.NotifyChange), 1, default), "[[{ \"name\": \"test.json\" }]]");

            var markup = _host.WaitForContains(component, "text-success");

            Assert.Contains("text-success", markup);
        }
Exemple #18
0
        public async void BeginInvokeDotNetFromJS(string callId, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson)
        {
            AssertInitialized();

            try
            {
                await Renderer.InvokeAsync(() =>
                {
                    SetCurrentCircuitHost(this);
                    DotNetDispatcher.BeginInvoke(callId, assemblyName, methodIdentifier, dotNetObjectId, argsJson);
                });
            }
            catch (Exception ex)
            {
                UnhandledException?.Invoke(this, new UnhandledExceptionEventArgs(ex, isTerminating: false));
            }
        }
Exemple #19
0
        public void CanInvokeStaticWithParams()
        {
            // Arrange
            var argsJson = Json.Serialize(new object[] {
                new TestDTO {
                    StringVal = "Another string", IntVal = 456
                },
                new[] { 100, 200 }
            });

            // Act
            var resultJson = DotNetDispatcher.Invoke(thisAssemblyName, "InvocableStaticWithParams", argsJson);
            var result     = Json.Deserialize <TestDTO>(resultJson);

            // Assert
            Assert.Equal("ANOTHER STRING", result.StringVal);
            Assert.Equal(756, result.IntVal);
        }
        public async Task SetCertificateAsync_should_get_certificate_thumbprint()
        {
            string relyingPartyId = await CreateEntity(null);

            var component = CreateComponent("Alice Smith",
                                            SharedConstants.WRITERPOLICY,
                                            relyingPartyId);

            var inputFile = component.FindComponent <InputFile>();
            await component.InvokeAsync(() => inputFile.Instance.OnChange.InvokeAsync(new InputFileChangeEventArgs(new List <IBrowserFile>
            {
                new FakeBrowserFile()
            })).ConfigureAwait(false)).ConfigureAwait(false);

            DotNetDispatcher.BeginInvokeDotNet(new JSRuntimeImpl(), new DotNetInvocationInfo(null, "NotifyChange", 1, default), "[[{ \"name\": \"test.crt\" }]]");

            component.WaitForState(() => component.Markup.Contains("Invalid file"));

            Assert.Contains("Invalid file", component.Markup);
        }
        // Invoked via Mono's JS interop mechanism (invoke_method)
        private static void BeginInvokeDotNet(string callId, string assemblyNameOrDotNetObjectId, string methodIdentifier, string argsJson)
        {
            // Figure out whether 'assemblyNameOrDotNetObjectId' is the assembly name or the instance ID
            // We only need one for any given call. This helps to work around the limitation that we can
            // only pass a maximum of 4 args in a call from JS to Mono WebAssembly.
            string assemblyName;
            long   dotNetObjectId;

            if (char.IsDigit(assemblyNameOrDotNetObjectId[0]))
            {
                dotNetObjectId = long.Parse(assemblyNameOrDotNetObjectId);
                assemblyName   = null;
            }
            else
            {
                dotNetObjectId = default;
                assemblyName   = assemblyNameOrDotNetObjectId;
            }

            DotNetDispatcher.BeginInvoke(callId, assemblyName, methodIdentifier, dotNetObjectId, argsJson);
        }
Exemple #22
0
        // ReceiveByteArray is used in a fire-and-forget context, so it's responsible for its own
        // error handling.
        internal async Task ReceiveByteArray(int id, byte[] data)
        {
            AssertInitialized();
            AssertNotDisposed();

            try
            {
                await Renderer.Dispatcher.InvokeAsync(() =>
                {
                    Log.ReceiveByteArraySuccess(_logger, id);
                    DotNetDispatcher.ReceiveByteArray(JSRuntime, id, data);
                });
            }
            catch (Exception ex)
            {
                // An error completing JS interop means that the user sent invalid data, a well-behaved
                // client won't do this.
                Log.ReceiveByteArrayException(_logger, id, ex);
                await TryNotifyClientErrorAsync(Client, GetClientErrorMessage(ex, "Invalid byte array."));

                UnhandledException?.Invoke(this, new UnhandledExceptionEventArgs(ex, isTerminating: false));
            }
        }
Exemple #23
0
        // BeginInvokeDotNetFromJS is used in a fire-and-forget context, so it's responsible for its own
        // error handling.
        public async Task BeginInvokeDotNetFromJS(string callId, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson)
        {
            AssertInitialized();
            AssertNotDisposed();

            try
            {
                await Renderer.Dispatcher.InvokeAsync(() =>
                {
                    Log.BeginInvokeDotNet(_logger, callId, assemblyName, methodIdentifier, dotNetObjectId);
                    var invocationInfo = new DotNetInvocationInfo(assemblyName, methodIdentifier, dotNetObjectId, callId);
                    DotNetDispatcher.BeginInvokeDotNet(JSRuntime, invocationInfo, argsJson);
                });
            }
            catch (Exception ex)
            {
                // We don't expect any of this code to actually throw, because DotNetDispatcher.BeginInvoke doesn't throw
                // however, we still want this to get logged if we do.
                Log.BeginInvokeDotNetFailed(_logger, callId, assemblyName, methodIdentifier, dotNetObjectId, ex);
                await TryNotifyClientErrorAsync(Client, GetClientErrorMessage(ex, "Interop call failed."));

                UnhandledException?.Invoke(this, new UnhandledExceptionEventArgs(ex, isTerminating: false));
            }
        }
 public void CannotInvokeWithEmptyAssemblyName()
 {
     var ex = Assert.Throws <ArgumentException>(() =>
     {
         DotNetDispatcher.Invoke(" ", "SomeMethod", default, "[]");
 // Invoked via Mono's JS interop mechanism (invoke_method)
 public static void EndInvokeJS(string argsJson)
 => DotNetDispatcher.EndInvokeJS(Instance, argsJson);
        // The following methods are invoke via Mono's JS interop mechanism (invoke_method)
        public static string?InvokeDotNet(string assemblyName, string methodIdentifier, string dotNetObjectId, string argsJson)
        {
            var callInfo = new DotNetInvocationInfo(assemblyName, methodIdentifier, dotNetObjectId == null ? default : long.Parse(dotNetObjectId, CultureInfo.InvariantCulture), callId: null);

            return(DotNetDispatcher.Invoke(Instance, callInfo, argsJson));
        }
 // Invoked via Mono's JS interop mechanism (invoke_method)
 private static string InvokeDotNet(string assemblyName, string methodIdentifier, string dotNetObjectId, string argsJson)
 => DotNetDispatcher.Invoke(assemblyName, methodIdentifier, dotNetObjectId == null ? default : long.Parse(dotNetObjectId), argsJson);
Exemple #28
0
 private static void ReceiveByteArrayFromJS(PageContext pageContext, int id, byte[] data)
 {
     DotNetDispatcher.ReceiveByteArray(pageContext.JSRuntime, id, data);
 }
Exemple #29
0
 private static void EndInvokeJS(PageContext pageContext, string argumentsOrError)
 {
     DotNetDispatcher.EndInvokeJS(pageContext.JSRuntime, argumentsOrError);
 }
Exemple #30
0
 private void EndInvokeJS(PageContext pageContext, long asyncHandle, bool succeeded, string argumentsOrError)
 {
     DotNetDispatcher.EndInvokeJS(pageContext.JSRuntime, argumentsOrError);
 }