/// <summary> /// Performs the handshake with the javascript part of blazor. /// </summary> /// <param name="ipc">The IPC channel to communicate between blazor and javascript.</param> /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> private static async Task PerformHandshakeAsync(IPC ipc, ManualResetEventSlim completed) { var tcs = new TaskCompletionSource <object>(); ipc.Once("components:init", args => { var argsArray = (object[])args; InitialUriAbsolute = ((JsonElement)argsArray[0]).GetString(); BaseUriAbsolute = ((JsonElement)argsArray[1]).GetString(); tcs.SetResult(null); }); completed.Set(); await tcs.Task; }
/// <summary> /// Runs Blazor using the specified <see cref="IBlazorWebView"/> implementation, /// using the path to the index.html host file and optionally a delegate to /// resolve the normal (non framework) resources. /// </summary> /// <typeparam name="TStartup">A startup class.</typeparam> /// <param name="blazorWebView">An <see cref="IBlazorWebView"/> implementation.</param> /// <param name="hostHtmlPath">The specified oath to the index.html file.</param> /// <param name="defaultResolveDelegate">An optional delegate to resolve non framework resources.</param> /// <returns>An <see cref="IDisposable "/> instance that can be used to cleanup Blazor.</returns> public static IDisposable Run <TStartup>(IBlazorWebView blazorWebView, string hostHtmlPath, ResolveWebResourceDelegate defaultResolveDelegate = null) { if (defaultResolveDelegate == null) { var contentRootAbsolute = Path.GetDirectoryName(Path.GetFullPath(hostHtmlPath)); defaultResolveDelegate = (string url, out string contentType, out Encoding encoding) => { // TODO: Only intercept for the hostname 'app' and passthrough for others // TODO: Prevent directory traversal? var appFile = Path.Combine(contentRootAbsolute, new Uri(url).AbsolutePath.Substring(1)); if (appFile == contentRootAbsolute) { appFile = hostHtmlPath; } contentType = GetContentType(appFile); if (!File.Exists(appFile)) { encoding = Encoding.Default; return(null); } return(GetEncodingAndOpen(appFile, out encoding)); }; } BlazorWebView = blazorWebView; BlazorWebView.Initialize(options => { options.SchemeHandlers.Add(BlazorAppScheme, defaultResolveDelegate); // framework:// is resolved as embedded resources options.SchemeHandlers.Add("framework", (string url, out string contentType, out Encoding encoding) => { contentType = GetContentType(url); encoding = Encoding.UTF8; return(SupplyFrameworkFile(url)); }); }); CancellationTokenSource appLifetimeCts = new CancellationTokenSource(); Task.Factory.StartNew(async() => { try { var ipc = new IPC(BlazorWebView); await RunAsync <TStartup>(ipc, appLifetimeCts.Token); } catch (Exception ex) { UnhandledException(ex); throw; } }); try { BlazorWebView.NavigateToUrl(BlazorAppScheme + "://app/"); } catch { appLifetimeCts.Cancel(); throw; } return(new DelegateDisposable(() => appLifetimeCts.Cancel())); }
/// <summary> /// Initializes a new instance of the <see cref="PlatformJSRuntime"/> class. /// </summary> /// <param name="ipc">The inter process communication channel to use.</param> public PlatformJSRuntime(IPC ipc) { this.ipc = ipc ?? throw new ArgumentNullException(nameof(ipc)); }
/// <summary> /// Initializes a new instance of the <see cref="PlatformJSRuntime"/> class. /// </summary> /// <param name="ipc">The inter process communication channel to use.</param> public PlatformJSRuntime(IPC ipc) { this.JsonSerializerOptions.Converters.Add(new ElementReferenceJsonConverter()); this.ipc = ipc ?? throw new ArgumentNullException(nameof(ipc)); }