File: Services\DefaultWebAssemblyJSRuntime.cs
Web Access
Project: src\src\Components\WebAssembly\WebAssembly\src\Microsoft.AspNetCore.Components.WebAssembly.csproj (Microsoft.AspNetCore.Components.WebAssembly)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
using Microsoft.AspNetCore.Components.Web.Internal;
using Microsoft.AspNetCore.Components.WebAssembly.Infrastructure;
using Microsoft.JSInterop;
using Microsoft.JSInterop.Infrastructure;
using Microsoft.JSInterop.WebAssembly;
using static Microsoft.AspNetCore.Internal.LinkerFlags;
 
namespace Microsoft.AspNetCore.Components.WebAssembly.Services;
 
internal sealed partial class DefaultWebAssemblyJSRuntime : WebAssemblyJSRuntime, IInternalWebJSInProcessRuntime
{
    public static readonly DefaultWebAssemblyJSRuntime Instance = new();
 
    private readonly RootTypeCache _rootComponentCache = new();
 
    public ElementReferenceContext ElementReferenceContext { get; }
 
    public event Action<RootComponentOperationBatch, string>? OnUpdateRootComponents;
 
    [DynamicDependency(nameof(InvokeDotNet))]
    [DynamicDependency(nameof(EndInvokeJS))]
    [DynamicDependency(nameof(BeginInvokeDotNet))]
    [DynamicDependency(nameof(ReceiveByteArrayFromJS))]
    [DynamicDependency(nameof(UpdateRootComponents))]
    [DynamicDependency(nameof(DispatchMouseEvent))]
    [DynamicDependency(nameof(DispatchDragEvent))]
    [DynamicDependency(nameof(DispatchKeyboardEvent))]
    [DynamicDependency(nameof(DispatchChangeEventString))]
    [DynamicDependency(nameof(DispatchChangeEventBool))]
    [DynamicDependency(nameof(DispatchChangeEventStringArray))]
    [DynamicDependency(nameof(DispatchFocusEvent))]
    [DynamicDependency(nameof(DispatchClipboardEvent))]
    [DynamicDependency(nameof(DispatchPointerEvent))]
    [DynamicDependency(nameof(DispatchWheelEvent))]
    [DynamicDependency(nameof(DispatchTouchEvent))]
    [DynamicDependency(nameof(DispatchProgressEvent))]
    [DynamicDependency(nameof(DispatchErrorEvent))]
    [DynamicDependency(nameof(DispatchEmptyEvent))]
    [DynamicDependency(nameof(DispatchEventJson))]
    [DynamicDependency(nameof(DispatchLocationChanged))]
    [DynamicDependency(nameof(DispatchLocationChanging))]
    [DynamicDependency(JsonSerialized, typeof(KeyValuePair<,>))]
    private DefaultWebAssemblyJSRuntime()
    {
        ElementReferenceContext = new WebElementReferenceContext(this);
        JsonSerializerOptions.Converters.Add(new ElementReferenceJsonConverter(ElementReferenceContext));
    }
 
    public JsonSerializerOptions ReadJsonSerializerOptions() => JsonSerializerOptions;
 
    [DynamicDependency(JsonSerialized, typeof(RootComponentOperation))]
    [DynamicDependency(JsonSerialized, typeof(RootComponentOperationBatch))]
    [DynamicDependency(JsonSerialized, typeof(ComponentMarkerKey))]
    [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "The correct members will be preserved by the above DynamicDependency")]
    internal static RootComponentOperationBatch DeserializeOperations(string operationsJson)
    {
        var deserialized = JsonSerializer.Deserialize(
            operationsJson,
            WebAssemblyJsonSerializerContext.Default.RootComponentOperationBatch)!;
 
        for (var i = 0; i < deserialized.Operations.Length; i++)
        {
            var operation = deserialized.Operations[i];
            if (operation.Type == RootComponentOperationType.Remove)
            {
                continue;
            }
 
            if (operation.Marker == null)
            {
                throw new InvalidOperationException($"The component operation of type '{operation.Type}' requires a '{nameof(operation.Marker)}' to be specified.");
            }
 
            var componentType = Instance._rootComponentCache.GetRootType(operation.Marker!.Value.Assembly!, operation.Marker.Value.TypeName!)
                ?? throw new InvalidOperationException($"Root component type '{operation.Marker.Value.TypeName}' could not be found in the assembly '{operation.Marker.Value.Assembly}'.");
            var parameters = DeserializeComponentParameters(operation.Marker.Value);
            operation.Descriptor = new(componentType, parameters);
        }
 
        return deserialized;
    }
 
    static WebRootComponentParameters DeserializeComponentParameters(ComponentMarker marker)
    {
        var definitions = WebAssemblyComponentParameterDeserializer.GetParameterDefinitions(marker.ParameterDefinitions!);
        var values = WebAssemblyComponentParameterDeserializer.GetParameterValues(marker.ParameterValues!);
        var componentDeserializer = WebAssemblyComponentParameterDeserializer.Instance;
        var parameters = componentDeserializer.DeserializeParameters(definitions, values);
 
        return new(parameters, definitions, values.AsReadOnly());
    }
 
    /// <inheritdoc />
    protected override Task<Stream> ReadJSDataAsStreamAsync(IJSStreamReference jsStreamReference, long totalLength, CancellationToken cancellationToken = default)
        => Task.FromResult<Stream>(PullFromJSDataStream.CreateJSDataStream(this, jsStreamReference, totalLength, cancellationToken));
 
    /// <inheritdoc />
    protected override Task TransmitStreamAsync(long streamId, DotNetStreamReference dotNetStreamReference)
    {
        return TransmitDataStreamToJS.TransmitStreamAsync(this, "Blazor._internal.receiveWebAssemblyDotNetDataStream", streamId, dotNetStreamReference);
    }
 
    string IInternalWebJSInProcessRuntime.InvokeJS(string identifier, string? argsJson, JSCallResultType resultType, long targetInstanceId)
        => InvokeJS(identifier, argsJson, resultType, targetInstanceId);
 
    string IInternalWebJSInProcessRuntime.InvokeJS(in JSInvocationInfo invocationInfo)
        => InvokeJS(invocationInfo);
}