// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.AspNetCore.Blazor.Browser.Services; using Microsoft.AspNetCore.Blazor.Components; using Microsoft.AspNetCore.Blazor.Rendering; using Microsoft.JSInterop; using Mono.WebAssembly.Interop; using System; namespace Microsoft.AspNetCore.Blazor.Browser.Rendering { /// /// Provides mechanisms for rendering instances in a /// web browser, dispatching events to them, and refreshing the UI as required. /// public class BrowserRenderer : Renderer, IDisposable { private readonly int _browserRendererId; /// /// Constructs an instance of . /// public BrowserRenderer(): this(new BrowserServiceProvider()) { } /// /// Constructs an instance of . /// /// The to use when initializing components. public BrowserRenderer(IServiceProvider serviceProvider): base(serviceProvider) { // The browser renderer registers and unregisters itself with the static // registry. This works well with the WebAssembly runtime, and is simple for the // case where Blazor is running in process. _browserRendererId = RendererRegistry.Current.Add(this); } internal void DispatchBrowserEvent(int componentId, int eventHandlerId, UIEventArgs eventArgs) => DispatchEvent(componentId, eventHandlerId, eventArgs); /// /// Attaches a new root component to the renderer, /// causing it to be displayed in the specified DOM element. /// /// The type of the component. /// A CSS selector that uniquely identifies a DOM element. public void AddComponent(string domElementSelector) where TComponent: IComponent { AddComponent(typeof(TComponent), domElementSelector); } /// /// Associates the with the , /// causing it to be displayed in the specified DOM element. /// /// The type of the component. /// A CSS selector that uniquely identifies a DOM element. public void AddComponent(Type componentType, string domElementSelector) { var component = InstantiateComponent(componentType); var componentId = AssignRootComponentId(component); // The only reason we're calling this synchronously is so that, if it throws, // we get the exception back *before* attempting the first UpdateDisplay // (otherwise the logged exception will come from UpdateDisplay instead of here) // When implementing support for out-of-process runtimes, we'll need to call this // asynchronously and ensure we surface any exceptions correctly. ((IJSInProcessRuntime)JSRuntime.Current).Invoke( "Blazor._internal.attachRootComponentToElement", _browserRendererId, domElementSelector, componentId); component.SetParameters(ParameterCollection.Empty); } /// /// Disposes the instance. /// public void Dispose() { RendererRegistry.Current.TryRemove(_browserRendererId); } /// protected override void UpdateDisplay(in RenderBatch batch) { if (JSRuntime.Current is MonoWebAssemblyJSRuntime mono) { mono.InvokeUnmarshalled( "Blazor._internal.renderBatch", _browserRendererId, batch); } else { // When implementing support for an out-of-process JS runtime, we'll need to // do something here to serialize and transmit the RenderBatch efficiently. throw new NotImplementedException("TODO: Support BrowserRenderer.UpdateDisplay on other runtimes."); } } } }