* Remove IVTs from Microsoft.AspNetCore.Components.Web to .Blazor/.Server * Eliminate RendererRegistry from .Web, as it's not needed for server-side Blazor * Reintroduce RendererRegistry as a WebAssembly-specific concept. Refactor event data parsing. * Remove redundant guard * Corresponding test updates * Update ref sources * CR: Remove renderer ID fully from server-side Blazor code * CR: Make WebEventData internal and shared-source * Hub test updates * Clean whitespace * Update binaries * Regenerate binaries AGAIN * Update Jest test * CR: Replace constructor with static parse * Yet again attempt to refresh the .js binaries * Fix ref assembly * Fix test
This commit is contained in:
parent
4c49a9206e
commit
d716ca9d62
|
|
@ -65,16 +65,10 @@ namespace Microsoft.AspNetCore.Blazor.Http
|
|||
}
|
||||
namespace Microsoft.AspNetCore.Blazor.Rendering
|
||||
{
|
||||
public partial class WebAssemblyRenderer : Microsoft.AspNetCore.Components.Rendering.Renderer
|
||||
public static partial class WebAssemblyEventDispatcher
|
||||
{
|
||||
public WebAssemblyRenderer(System.IServiceProvider serviceProvider, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) : base (default(System.IServiceProvider), default(Microsoft.Extensions.Logging.ILoggerFactory)) { }
|
||||
public override Microsoft.AspNetCore.Components.Dispatcher Dispatcher { get { throw null; } }
|
||||
public System.Threading.Tasks.Task AddComponentAsync(System.Type componentType, string domElementSelector) { throw null; }
|
||||
public System.Threading.Tasks.Task AddComponentAsync<TComponent>(string domElementSelector) where TComponent : Microsoft.AspNetCore.Components.IComponent { throw null; }
|
||||
public override System.Threading.Tasks.Task DispatchEventAsync(ulong eventHandlerId, Microsoft.AspNetCore.Components.Rendering.EventFieldInfo eventFieldInfo, System.EventArgs eventArgs) { throw null; }
|
||||
protected override void Dispose(bool disposing) { }
|
||||
protected override void HandleException(System.Exception exception) { }
|
||||
protected override System.Threading.Tasks.Task UpdateDisplayAsync(in Microsoft.AspNetCore.Components.Rendering.RenderBatch batch) { throw null; }
|
||||
[Microsoft.JSInterop.JSInvokableAttribute("DispatchEvent")]
|
||||
public static System.Threading.Tasks.Task DispatchEvent(Microsoft.AspNetCore.Components.Web.WebEventDescriptor eventDescriptor, string eventArgsJson) { throw null; }
|
||||
}
|
||||
}
|
||||
namespace Microsoft.AspNetCore.Components.Builder
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
|
|
@ -11,4 +11,10 @@
|
|||
<Reference Include="Microsoft.AspNetCore.Components.Web" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\..\..\Shared\src\BrowserNavigationManagerInterop.cs" />
|
||||
<Compile Include="..\..\..\Shared\src\JsonSerializerOptionsProvider.cs" />
|
||||
<Compile Include="..\..\..\Shared\src\WebEventData.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
// 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 System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Rendering
|
||||
{
|
||||
internal static class RendererRegistry
|
||||
{
|
||||
// In case there are multiple concurrent Blazor renderers in the same .NET WebAssembly
|
||||
// process, we track them by ID. This allows events to be dispatched to the correct one,
|
||||
// as well as rooting them for GC purposes, since nothing would otherwise be referencing
|
||||
// them even though we might still receive incoming events from JS.
|
||||
|
||||
private static int _nextId;
|
||||
private static Dictionary<int, WebAssemblyRenderer> _renderers = new Dictionary<int, WebAssemblyRenderer>();
|
||||
|
||||
internal static WebAssemblyRenderer Find(int rendererId)
|
||||
{
|
||||
return _renderers.ContainsKey(rendererId)
|
||||
? _renderers[rendererId]
|
||||
: throw new ArgumentException($"There is no renderer with ID {rendererId}.");
|
||||
}
|
||||
|
||||
public static int Add(WebAssemblyRenderer renderer)
|
||||
{
|
||||
var id = _nextId++;
|
||||
_renderers.Add(id, renderer);
|
||||
return id;
|
||||
}
|
||||
|
||||
public static bool TryRemove(int rendererId)
|
||||
{
|
||||
if (_renderers.ContainsKey(rendererId))
|
||||
{
|
||||
_renderers.Remove(rendererId);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
// 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 System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Rendering
|
||||
{
|
||||
/// <summary>
|
||||
/// Dispatches events from JavaScript to a Blazor WebAssembly renderer.
|
||||
/// Intended for internal use only.
|
||||
/// </summary>
|
||||
public static class WebAssemblyEventDispatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// For framework use only.
|
||||
/// </summary>
|
||||
[JSInvokable(nameof(DispatchEvent))]
|
||||
public static Task DispatchEvent(WebEventDescriptor eventDescriptor, string eventArgsJson)
|
||||
{
|
||||
var webEvent = WebEventData.Parse(eventDescriptor, eventArgsJson);
|
||||
var renderer = RendererRegistry.Find(eventDescriptor.BrowserRendererId);
|
||||
return renderer.DispatchEventAsync(
|
||||
webEvent.EventHandlerId,
|
||||
webEvent.EventFieldInfo,
|
||||
webEvent.EventArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,6 @@ using System.Collections.Generic;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Blazor.Services;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using Microsoft.AspNetCore.Components.Rendering;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
|
|
@ -16,7 +15,7 @@ namespace Microsoft.AspNetCore.Blazor.Rendering
|
|||
/// Provides mechanisms for rendering <see cref="IComponent"/> instances in a
|
||||
/// web browser, dispatching events to them, and refreshing the UI as required.
|
||||
/// </summary>
|
||||
public class WebAssemblyRenderer : Renderer
|
||||
internal class WebAssemblyRenderer : Renderer
|
||||
{
|
||||
private readonly int _webAssemblyRendererId;
|
||||
|
||||
|
|
@ -31,10 +30,8 @@ namespace Microsoft.AspNetCore.Blazor.Rendering
|
|||
public WebAssemblyRenderer(IServiceProvider serviceProvider, ILoggerFactory loggerFactory)
|
||||
: base(serviceProvider, loggerFactory)
|
||||
{
|
||||
// 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.
|
||||
_webAssemblyRendererId = RendererRegistry.Current.Add(this);
|
||||
// The WebAssembly renderer registers and unregisters itself with the static registry
|
||||
_webAssemblyRendererId = RendererRegistry.Add(this);
|
||||
}
|
||||
|
||||
public override Dispatcher Dispatcher => NullDispatcher.Instance;
|
||||
|
|
@ -77,9 +74,9 @@ namespace Microsoft.AspNetCore.Blazor.Rendering
|
|||
|
||||
WebAssemblyJSRuntime.Instance.Invoke<object>(
|
||||
"Blazor._internal.attachRootComponentToElement",
|
||||
_webAssemblyRendererId,
|
||||
domElementSelector,
|
||||
componentId);
|
||||
componentId,
|
||||
_webAssemblyRendererId);
|
||||
|
||||
return RenderRootComponentAsync(componentId);
|
||||
}
|
||||
|
|
@ -88,7 +85,7 @@ namespace Microsoft.AspNetCore.Blazor.Rendering
|
|||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
RendererRegistry.Current.TryRemove(_webAssemblyRendererId);
|
||||
RendererRegistry.TryRemove(_webAssemblyRendererId);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Claims;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
|
|
@ -41,7 +40,6 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
}
|
||||
|
||||
JSInterop.JSRuntime.SetCurrentJSRuntime(circuitHost.JSRuntime);
|
||||
RendererRegistry.SetCurrentRendererRegistry(circuitHost.RendererRegistry);
|
||||
}
|
||||
|
||||
public event UnhandledExceptionEventHandler UnhandledException;
|
||||
|
|
@ -50,7 +48,6 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
string circuitId,
|
||||
IServiceScope scope,
|
||||
CircuitClientProxy client,
|
||||
RendererRegistry rendererRegistry,
|
||||
RemoteRenderer renderer,
|
||||
IReadOnlyList<ComponentDescriptor> descriptors,
|
||||
RemoteJSRuntime jsRuntime,
|
||||
|
|
@ -60,7 +57,6 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
CircuitId = circuitId;
|
||||
_scope = scope ?? throw new ArgumentNullException(nameof(scope));
|
||||
Client = client;
|
||||
RendererRegistry = rendererRegistry ?? throw new ArgumentNullException(nameof(rendererRegistry));
|
||||
Descriptors = descriptors ?? throw new ArgumentNullException(nameof(descriptors));
|
||||
Renderer = renderer ?? throw new ArgumentNullException(nameof(renderer));
|
||||
JSRuntime = jsRuntime ?? throw new ArgumentNullException(nameof(jsRuntime));
|
||||
|
|
@ -85,8 +81,6 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
|
||||
public RemoteRenderer Renderer { get; }
|
||||
|
||||
public RendererRegistry RendererRegistry { get; }
|
||||
|
||||
public IReadOnlyList<ComponentDescriptor> Descriptors { get; }
|
||||
|
||||
public IServiceProvider Services { get; }
|
||||
|
|
@ -137,46 +131,38 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
}
|
||||
}
|
||||
|
||||
public async Task DispatchEvent(string eventDescriptorJson, string eventArgs)
|
||||
public async Task DispatchEvent(string eventDescriptorJson, string eventArgsJson)
|
||||
{
|
||||
RendererRegistryEventDispatcher.BrowserEventDescriptor eventDescriptor = null;
|
||||
WebEventData webEventData;
|
||||
try
|
||||
{
|
||||
AssertInitialized();
|
||||
eventDescriptor = ParseEventDescriptor(eventDescriptorJson);
|
||||
if (eventDescriptor == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
webEventData = WebEventData.Parse(eventDescriptorJson, eventArgsJson);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.DispatchEventFailedToParseEventData(_logger, ex);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await Renderer.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
SetCurrentCircuitHost(this);
|
||||
return RendererRegistryEventDispatcher.DispatchEvent(eventDescriptor, eventArgs);
|
||||
return Renderer.DispatchEventAsync(
|
||||
webEventData.EventHandlerId,
|
||||
webEventData.EventFieldInfo,
|
||||
webEventData.EventArgs);
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.DispatchEventFailedToDispatchEvent(_logger, eventDescriptor != null ? eventDescriptor.EventHandlerId.ToString() : null, ex);
|
||||
Log.DispatchEventFailedToDispatchEvent(_logger, webEventData.EventHandlerId.ToString(), ex);
|
||||
UnhandledException?.Invoke(this, new UnhandledExceptionEventArgs(ex, isTerminating: false));
|
||||
}
|
||||
}
|
||||
|
||||
private RendererRegistryEventDispatcher.BrowserEventDescriptor ParseEventDescriptor(string eventDescriptorJson)
|
||||
{
|
||||
try
|
||||
{
|
||||
return JsonSerializer.Deserialize<RendererRegistryEventDispatcher.BrowserEventDescriptor>(
|
||||
eventDescriptorJson,
|
||||
JsonSerializerOptionsProvider.Options);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.DispatchEventFailedToParseEventDescriptor(_logger, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task InitializeAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
await Renderer.Dispatcher.InvokeAsync(async () =>
|
||||
|
|
@ -214,11 +200,6 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
try
|
||||
{
|
||||
AssertInitialized();
|
||||
if (assemblyName == "Microsoft.AspNetCore.Components.Web" && methodIdentifier == "DispatchEvent")
|
||||
{
|
||||
Log.DispatchEventTroughJSInterop(_logger);
|
||||
return;
|
||||
}
|
||||
|
||||
await Renderer.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
|
|
@ -410,9 +391,8 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
private static readonly Action<ILogger, Exception> _endInvokeDispatchException;
|
||||
private static readonly Action<ILogger, long, string, Exception> _endInvokeJSFailed;
|
||||
private static readonly Action<ILogger, long, Exception> _endInvokeJSSucceeded;
|
||||
private static readonly Action<ILogger, Exception> _dispatchEventFailedToParseEventDescriptor;
|
||||
private static readonly Action<ILogger, Exception> _dispatchEventFailedToParseEventData;
|
||||
private static readonly Action<ILogger, string, Exception> _dispatchEventFailedToDispatchEvent;
|
||||
private static readonly Action<ILogger, Exception> _dispatchEventThroughJSInterop;
|
||||
private static readonly Action<ILogger, string, string, Exception> _locationChange;
|
||||
private static readonly Action<ILogger, string, string, Exception> _locationChangeSucceeded;
|
||||
private static readonly Action<ILogger, string, string, Exception> _locationChangeFailed;
|
||||
|
|
@ -426,7 +406,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
public static readonly EventId OnConnectionDown = new EventId(104, "OnConnectionDown");
|
||||
public static readonly EventId OnCircuitClosed = new EventId(105, "OnCircuitClosed");
|
||||
public static readonly EventId InvalidBrowserEventFormat = new EventId(106, "InvalidBrowserEventFormat");
|
||||
public static readonly EventId DispatchEventFailedToParseEventDescriptor = new EventId(107, "DispatchEventFailedToParseEventDescriptor");
|
||||
public static readonly EventId DispatchEventFailedToParseEventData = new EventId(107, "DispatchEventFailedToParseEventData");
|
||||
public static readonly EventId DispatchEventFailedToDispatchEvent = new EventId(108, "DispatchEventFailedToDispatchEvent");
|
||||
public static readonly EventId BeginInvokeDotNet = new EventId(109, "BeginInvokeDotNet");
|
||||
public static readonly EventId EndInvokeDispatchException = new EventId(110, "EndInvokeDispatchException");
|
||||
|
|
@ -495,21 +475,16 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
EventIds.EndInvokeJSSucceeded,
|
||||
"The JS interop call with callback id '{AsyncCall}' succeeded.");
|
||||
|
||||
_dispatchEventFailedToParseEventDescriptor = LoggerMessage.Define(
|
||||
_dispatchEventFailedToParseEventData = LoggerMessage.Define(
|
||||
LogLevel.Debug,
|
||||
EventIds.DispatchEventFailedToParseEventDescriptor,
|
||||
"Failed to parse the event descriptor data when trying to dispatch an event.");
|
||||
EventIds.DispatchEventFailedToParseEventData,
|
||||
"Failed to parse the event data when trying to dispatch an event.");
|
||||
|
||||
_dispatchEventFailedToDispatchEvent = LoggerMessage.Define<string>(
|
||||
LogLevel.Debug,
|
||||
EventIds.DispatchEventFailedToDispatchEvent,
|
||||
"There was an error dispatching the event '{EventHandlerId}' to the application.");
|
||||
|
||||
_dispatchEventThroughJSInterop = LoggerMessage.Define(
|
||||
LogLevel.Debug,
|
||||
EventIds.DispatchEventThroughJSInterop,
|
||||
"There was an intent to dispatch a browser event through JS interop.");
|
||||
|
||||
_locationChange = LoggerMessage.Define<string, string>(
|
||||
LogLevel.Debug,
|
||||
EventIds.LocationChange,
|
||||
|
|
@ -555,7 +530,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
|
||||
public static void EndInvokeJSSucceeded(ILogger logger, long asyncCall) => _endInvokeJSSucceeded(logger, asyncCall, null);
|
||||
|
||||
public static void DispatchEventFailedToParseEventDescriptor(ILogger logger, Exception ex) => _dispatchEventFailedToParseEventDescriptor(logger, ex);
|
||||
public static void DispatchEventFailedToParseEventData(ILogger logger, Exception ex) => _dispatchEventFailedToParseEventData(logger, ex);
|
||||
|
||||
public static void DispatchEventFailedToDispatchEvent(ILogger logger, string eventHandlerId, Exception ex) => _dispatchEventFailedToDispatchEvent(logger, eventHandlerId ?? "", ex);
|
||||
|
||||
|
|
@ -571,8 +546,6 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
}
|
||||
}
|
||||
|
||||
public static void DispatchEventTroughJSInterop(ILogger logger) => _dispatchEventThroughJSInterop(logger, null);
|
||||
|
||||
public static void LocationChange(ILogger logger, string circuitId, string uri) => _locationChange(logger, circuitId, uri, null);
|
||||
|
||||
public static void LocationChangeSucceeded(ILogger logger, string circuitId, string uri) => _locationChangeSucceeded(logger, circuitId, uri, null);
|
||||
|
|
|
|||
|
|
@ -72,11 +72,9 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
navigationManager.Initialize(baseUri, uri);
|
||||
}
|
||||
|
||||
var rendererRegistry = new RendererRegistry();
|
||||
var renderer = new RemoteRenderer(
|
||||
scope.ServiceProvider,
|
||||
_loggerFactory,
|
||||
rendererRegistry,
|
||||
_options,
|
||||
jsRuntime,
|
||||
client,
|
||||
|
|
@ -91,7 +89,6 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
_circuitIdFactory.CreateCircuitId(),
|
||||
scope,
|
||||
client,
|
||||
rendererRegistry,
|
||||
renderer,
|
||||
components,
|
||||
jsRuntime,
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
|
|||
private readonly IJSRuntime _jsRuntime;
|
||||
private readonly CircuitClientProxy _client;
|
||||
private readonly CircuitOptions _options;
|
||||
private readonly RendererRegistry _rendererRegistry;
|
||||
private readonly ILogger _logger;
|
||||
internal readonly ConcurrentQueue<UnacknowledgedRenderBatch> _unacknowledgedRenderBatches = new ConcurrentQueue<UnacknowledgedRenderBatch>();
|
||||
private long _nextRenderId = 1;
|
||||
|
|
@ -41,7 +40,6 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
|
|||
public RemoteRenderer(
|
||||
IServiceProvider serviceProvider,
|
||||
ILoggerFactory loggerFactory,
|
||||
RendererRegistry rendererRegistry,
|
||||
CircuitOptions options,
|
||||
IJSRuntime jsRuntime,
|
||||
CircuitClientProxy client,
|
||||
|
|
@ -49,19 +47,14 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
|
|||
ILogger logger)
|
||||
: base(serviceProvider, loggerFactory, encoder.Encode)
|
||||
{
|
||||
_rendererRegistry = rendererRegistry;
|
||||
_jsRuntime = jsRuntime;
|
||||
_client = client;
|
||||
_options = options;
|
||||
|
||||
Id = _rendererRegistry.Add(this);
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override Dispatcher Dispatcher { get; } = Dispatcher.CreateDefault();
|
||||
|
||||
public int Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Associates the <see cref="IComponent"/> with the <see cref="RemoteRenderer"/>,
|
||||
/// causing it to be displayed in the specified DOM element.
|
||||
|
|
@ -75,7 +68,6 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
|
|||
|
||||
var attachComponentTask = _jsRuntime.InvokeAsync<object>(
|
||||
"Blazor._internal.attachRootComponentToElement",
|
||||
Id,
|
||||
domElementSelector,
|
||||
componentId);
|
||||
CaptureAsyncExceptions(attachComponentTask);
|
||||
|
|
@ -133,7 +125,6 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
|
|||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
_disposing = true;
|
||||
_rendererRegistry.TryRemove(Id);
|
||||
while (_unacknowledgedRenderBatches.TryDequeue(out var entry))
|
||||
{
|
||||
entry.CompletionSource.TrySetCanceled();
|
||||
|
|
@ -220,7 +211,7 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
|
|||
|
||||
Log.BeginUpdateDisplayAsync(_logger, _client.ConnectionId, pending.BatchId, pending.Data.Count);
|
||||
var segment = new ArraySegment<byte>(pending.Data.Buffer, 0, pending.Data.Count);
|
||||
await _client.SendAsync("JS.RenderBatch", Id, pending.BatchId, segment);
|
||||
await _client.SendAsync("JS.RenderBatch", pending.BatchId, segment);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -41,6 +41,10 @@
|
|||
<Compile Include="$(ComponentsSharedSourceRoot)src\CacheHeaderSettings.cs" Link="Shared\CacheHeaderSettings.cs" />
|
||||
<Compile Include="$(ComponentsSharedSourceRoot)src\ArrayBuilder.cs" LinkBase="Circuits" />
|
||||
|
||||
<Compile Include="..\..\Shared\src\BrowserNavigationManagerInterop.cs" />
|
||||
<Compile Include="..\..\Shared\src\JsonSerializerOptionsProvider.cs" />
|
||||
<Compile Include="..\..\Shared\src\WebEventData.cs" />
|
||||
|
||||
<Compile Include="$(RepoRoot)src\SignalR\common\Shared\BinaryMessageFormatter.cs" LinkBase="BlazorPack" />
|
||||
<Compile Include="$(RepoRoot)src\SignalR\common\Shared\BinaryMessageParser.cs" LinkBase="BlazorPack" />
|
||||
<Compile Include="$(RepoRoot)src\SignalR\common\Shared\MemoryBufferWriter.cs" LinkBase="BlazorPack" />
|
||||
|
|
|
|||
|
|
@ -232,15 +232,14 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
{
|
||||
return new TestRemoteRenderer(
|
||||
Mock.Of<IServiceProvider>(),
|
||||
new RendererRegistry(),
|
||||
Mock.Of<IJSRuntime>(),
|
||||
Mock.Of<IClientProxy>());
|
||||
}
|
||||
|
||||
private class TestRemoteRenderer : RemoteRenderer
|
||||
{
|
||||
public TestRemoteRenderer(IServiceProvider serviceProvider, RendererRegistry rendererRegistry, IJSRuntime jsRuntime, IClientProxy client)
|
||||
: base(serviceProvider, NullLoggerFactory.Instance, rendererRegistry, new CircuitOptions(), jsRuntime, new CircuitClientProxy(client, "connection"), HtmlEncoder.Default, NullLogger.Instance)
|
||||
public TestRemoteRenderer(IServiceProvider serviceProvider, IJSRuntime jsRuntime, IClientProxy client)
|
||||
: base(serviceProvider, NullLoggerFactory.Instance, new CircuitOptions(), jsRuntime, new CircuitClientProxy(client, "connection"), HtmlEncoder.Default, NullLogger.Instance)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
|
|||
|
||||
var initialClient = new Mock<IClientProxy>();
|
||||
initialClient.Setup(c => c.SendCoreAsync(It.IsAny<string>(), It.IsAny<object[]>(), It.IsAny<CancellationToken>()))
|
||||
.Callback((string name, object[] value, CancellationToken token) => renderIds.Add((long)value[1]))
|
||||
.Callback((string name, object[] value, CancellationToken token) => renderIds.Add((long)value[0]))
|
||||
.Returns(firstBatchTCS.Task);
|
||||
var circuitClient = new CircuitClientProxy(initialClient.Object, "connection0");
|
||||
var renderer = GetRemoteRenderer(serviceProvider, circuitClient);
|
||||
|
|
@ -155,8 +155,8 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
|
|||
|
||||
var client = new Mock<IClientProxy>();
|
||||
client.Setup(c => c.SendCoreAsync(It.IsAny<string>(), It.IsAny<object[]>(), It.IsAny<CancellationToken>()))
|
||||
.Callback((string name, object[] value, CancellationToken token) => renderIds.Add((long)value[1]))
|
||||
.Returns<string, object[], CancellationToken>((n, v, t) => (long)v[1] == 3 ? secondBatchTCS.Task : thirdBatchTCS.Task);
|
||||
.Callback((string name, object[] value, CancellationToken token) => renderIds.Add((long)value[0]))
|
||||
.Returns<string, object[], CancellationToken>((n, v, t) => (long)v[0] == 3 ? secondBatchTCS.Task : thirdBatchTCS.Task);
|
||||
|
||||
var componentId = renderer.AssignRootComponentId(component);
|
||||
component.TriggerRender();
|
||||
|
|
@ -465,7 +465,6 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
|
|||
return new RemoteRenderer(
|
||||
serviceProvider,
|
||||
NullLoggerFactory.Instance,
|
||||
new RendererRegistry(),
|
||||
new CircuitOptions(),
|
||||
jsRuntime.Object,
|
||||
circuitClientProxy,
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
{
|
||||
internal class TestCircuitHost : CircuitHost
|
||||
{
|
||||
private TestCircuitHost(string circuitId, IServiceScope scope, CircuitClientProxy client, RendererRegistry rendererRegistry, RemoteRenderer renderer, IReadOnlyList<ComponentDescriptor> descriptors, RemoteJSRuntime jsRuntime, CircuitHandler[] circuitHandlers, ILogger logger)
|
||||
: base(circuitId, scope, client, rendererRegistry, renderer, descriptors, jsRuntime, circuitHandlers, logger)
|
||||
private TestCircuitHost(string circuitId, IServiceScope scope, CircuitClientProxy client, RemoteRenderer renderer, IReadOnlyList<ComponentDescriptor> descriptors, RemoteJSRuntime jsRuntime, CircuitHandler[] circuitHandlers, ILogger logger)
|
||||
: base(circuitId, scope, client, renderer, descriptors, jsRuntime, circuitHandlers, logger)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -37,7 +37,6 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
{
|
||||
serviceScope = serviceScope ?? Mock.Of<IServiceScope>();
|
||||
clientProxy = clientProxy ?? new CircuitClientProxy(Mock.Of<IClientProxy>(), Guid.NewGuid().ToString());
|
||||
var renderRegistry = new RendererRegistry();
|
||||
var jsRuntime = new RemoteJSRuntime(Options.Create(new CircuitOptions()), Mock.Of<ILogger<RemoteJSRuntime>>());
|
||||
|
||||
if (remoteRenderer == null)
|
||||
|
|
@ -45,7 +44,6 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
remoteRenderer = new RemoteRenderer(
|
||||
serviceScope.ServiceProvider ?? Mock.Of<IServiceProvider>(),
|
||||
NullLoggerFactory.Instance,
|
||||
new RendererRegistry(),
|
||||
new CircuitOptions(),
|
||||
jsRuntime,
|
||||
clientProxy,
|
||||
|
|
@ -58,7 +56,6 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
circuitId ?? Guid.NewGuid().ToString(),
|
||||
serviceScope,
|
||||
clientProxy,
|
||||
renderRegistry,
|
||||
remoteRenderer,
|
||||
new List<ComponentDescriptor>(),
|
||||
jsRuntime,
|
||||
|
|
|
|||
|
|
@ -3,57 +3,47 @@
|
|||
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Components.Rendering;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace Microsoft.AspNetCore.Components.Web
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides mechanisms for dispatching events to components in a <see cref="Renderer"/>.
|
||||
/// </summary>
|
||||
public static class RendererRegistryEventDispatcher
|
||||
internal class WebEventData
|
||||
{
|
||||
/// <summary>
|
||||
/// For framework use only.
|
||||
/// </summary>
|
||||
[JSInvokable(nameof(DispatchEvent))]
|
||||
public static Task DispatchEvent(BrowserEventDescriptor eventDescriptor, string eventArgsJson)
|
||||
// This class represents the second half of parsing incoming event data,
|
||||
// once the type of the eventArgs becomes known.
|
||||
|
||||
public static WebEventData Parse(string eventDescriptorJson, string eventArgsJson)
|
||||
{
|
||||
InterpretEventDescriptor(eventDescriptor);
|
||||
var eventArgs = ParseEventArgsJson(eventDescriptor.EventArgsType, eventArgsJson);
|
||||
var renderer = RendererRegistry.Current.Find(eventDescriptor.BrowserRendererId);
|
||||
return renderer.DispatchEventAsync(eventDescriptor.EventHandlerId, eventDescriptor.EventFieldInfo, eventArgs);
|
||||
return Parse(
|
||||
Deserialize<WebEventDescriptor>(eventDescriptorJson),
|
||||
eventArgsJson);
|
||||
}
|
||||
|
||||
private static void InterpretEventDescriptor(BrowserEventDescriptor eventDescriptor)
|
||||
public static WebEventData Parse(WebEventDescriptor eventDescriptor, string eventArgsJson)
|
||||
{
|
||||
// The incoming field value can be either a bool or a string, but since the .NET property
|
||||
// type is 'object', it will deserialize initially as a JsonElement
|
||||
var fieldInfo = eventDescriptor.EventFieldInfo;
|
||||
if (fieldInfo != null)
|
||||
{
|
||||
if (fieldInfo.FieldValue is JsonElement attributeValueJsonElement)
|
||||
{
|
||||
switch (attributeValueJsonElement.ValueKind)
|
||||
{
|
||||
case JsonValueKind.True:
|
||||
case JsonValueKind.False:
|
||||
fieldInfo.FieldValue = attributeValueJsonElement.GetBoolean();
|
||||
break;
|
||||
default:
|
||||
fieldInfo.FieldValue = attributeValueJsonElement.GetString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unanticipated value type. Ensure we don't do anything with it.
|
||||
eventDescriptor.EventFieldInfo = null;
|
||||
}
|
||||
}
|
||||
return new WebEventData(
|
||||
eventDescriptor.BrowserRendererId,
|
||||
eventDescriptor.EventHandlerId,
|
||||
InterpretEventFieldInfo(eventDescriptor.EventFieldInfo),
|
||||
ParseEventArgsJson(eventDescriptor.EventArgsType, eventArgsJson));
|
||||
}
|
||||
|
||||
private WebEventData(int browserRendererId, ulong eventHandlerId, EventFieldInfo eventFieldInfo, EventArgs eventArgs)
|
||||
{
|
||||
BrowserRendererId = browserRendererId;
|
||||
EventHandlerId = eventHandlerId;
|
||||
EventFieldInfo = eventFieldInfo;
|
||||
EventArgs = eventArgs;
|
||||
}
|
||||
|
||||
public int BrowserRendererId { get; }
|
||||
|
||||
public ulong EventHandlerId { get; }
|
||||
|
||||
public EventFieldInfo EventFieldInfo { get; }
|
||||
|
||||
public EventArgs EventArgs { get; }
|
||||
|
||||
private static EventArgs ParseEventArgsJson(string eventArgsType, string eventArgsJson)
|
||||
{
|
||||
switch (eventArgsType)
|
||||
|
|
@ -83,13 +73,40 @@ namespace Microsoft.AspNetCore.Components.Web
|
|||
case "wheel":
|
||||
return Deserialize<UIWheelEventArgs>(eventArgsJson);
|
||||
default:
|
||||
throw new ArgumentException($"Unsupported value '{eventArgsType}'.", nameof(eventArgsType));
|
||||
throw new ArgumentException($"Unsupported value '{eventArgsType}'.", nameof(eventArgsType));
|
||||
}
|
||||
}
|
||||
|
||||
private static T Deserialize<T>(string eventArgsJson)
|
||||
private static T Deserialize<T>(string json)
|
||||
{
|
||||
return JsonSerializer.Deserialize<T>(eventArgsJson, JsonSerializerOptionsProvider.Options);
|
||||
return JsonSerializer.Deserialize<T>(json, JsonSerializerOptionsProvider.Options);
|
||||
}
|
||||
|
||||
private static EventFieldInfo InterpretEventFieldInfo(EventFieldInfo fieldInfo)
|
||||
{
|
||||
// The incoming field value can be either a bool or a string, but since the .NET property
|
||||
// type is 'object', it will deserialize initially as a JsonElement
|
||||
if (fieldInfo?.FieldValue is JsonElement attributeValueJsonElement)
|
||||
{
|
||||
switch (attributeValueJsonElement.ValueKind)
|
||||
{
|
||||
case JsonValueKind.True:
|
||||
case JsonValueKind.False:
|
||||
return new EventFieldInfo
|
||||
{
|
||||
ComponentId = fieldInfo.ComponentId,
|
||||
FieldValue = attributeValueJsonElement.GetBoolean()
|
||||
};
|
||||
default:
|
||||
return new EventFieldInfo
|
||||
{
|
||||
ComponentId = fieldInfo.ComponentId,
|
||||
FieldValue = attributeValueJsonElement.GetString()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static ChangeEventArgs DeserializeChangeEventArgs(string eventArgsJson)
|
||||
|
|
@ -113,31 +130,5 @@ namespace Microsoft.AspNetCore.Components.Web
|
|||
}
|
||||
return changeArgs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For framework use only.
|
||||
/// </summary>
|
||||
public class BrowserEventDescriptor
|
||||
{
|
||||
/// <summary>
|
||||
/// For framework use only.
|
||||
/// </summary>
|
||||
public int BrowserRendererId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// For framework use only.
|
||||
/// </summary>
|
||||
public ulong EventHandlerId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// For framework use only.
|
||||
/// </summary>
|
||||
public string EventArgsType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// For framework use only.
|
||||
/// </summary>
|
||||
public EventFieldInfo EventFieldInfo { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -85,12 +85,11 @@ async function initializeConnection(options: BlazorOptions, logger: Logger): Pro
|
|||
|
||||
connection.on('JS.BeginInvokeJS', DotNet.jsCallDispatcher.beginInvokeJSFromDotNet);
|
||||
connection.on('JS.EndInvokeDotNet', (args: string) => DotNet.jsCallDispatcher.endInvokeDotNetFromJS(...(JSON.parse(args) as [string, boolean, unknown])));
|
||||
connection.on('JS.RenderBatch', (browserRendererId: number, batchId: number, batchData: Uint8Array) => {
|
||||
logger.log(LogLevel.Debug, `Received render batch for ${browserRendererId} with id ${batchId} and ${batchData.byteLength} bytes.`);
|
||||
|
||||
const queue = RenderQueue.getOrCreateQueue(browserRendererId, logger);
|
||||
|
||||
queue.processBatch(batchId, batchData, connection);
|
||||
const renderQueue = new RenderQueue(/* renderer ID unused with remote renderer */ 0, logger);
|
||||
connection.on('JS.RenderBatch', (batchId: number, batchData: Uint8Array) => {
|
||||
logger.log(LogLevel.Debug, `Received render batch with id ${batchId} and ${batchData.byteLength} bytes.`);
|
||||
renderQueue.processBatch(batchId, batchData, connection);
|
||||
});
|
||||
|
||||
connection.onclose(error => !renderingFailed && options.reconnectionHandler!.onConnectionDown(options.reconnectionOptions, error));
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ async function boot(options?: any): Promise<void> {
|
|||
}
|
||||
started = true;
|
||||
|
||||
setEventDispatcher((eventDescriptor, eventArgs) => DotNet.invokeMethodAsync('Microsoft.AspNetCore.Components.Web', 'DispatchEvent', eventDescriptor, JSON.stringify(eventArgs)));
|
||||
setEventDispatcher((eventDescriptor, eventArgs) => DotNet.invokeMethodAsync('Microsoft.AspNetCore.Blazor', 'DispatchEvent', eventDescriptor, JSON.stringify(eventArgs)));
|
||||
|
||||
// Configure environment for execution under Mono WebAssembly with shared-memory rendering
|
||||
const platform = Environment.setPlatform(monoPlatform);
|
||||
|
|
|
|||
|
|
@ -4,8 +4,6 @@ import { Logger, LogLevel } from '../Logging/Logger';
|
|||
import { HubConnection } from '@aspnet/signalr';
|
||||
|
||||
export class RenderQueue {
|
||||
private static renderQueues = new Map<number, RenderQueue>();
|
||||
|
||||
private nextBatchId = 2;
|
||||
|
||||
private fatalError?: string;
|
||||
|
|
@ -19,17 +17,6 @@ export class RenderQueue {
|
|||
this.logger = logger;
|
||||
}
|
||||
|
||||
public static getOrCreateQueue(browserRendererId: number, logger: Logger): RenderQueue {
|
||||
const queue = this.renderQueues.get(browserRendererId);
|
||||
if (queue) {
|
||||
return queue;
|
||||
}
|
||||
|
||||
const newQueue = new RenderQueue(browserRendererId, logger);
|
||||
this.renderQueues.set(browserRendererId, newQueue);
|
||||
return newQueue;
|
||||
}
|
||||
|
||||
public async processBatch(receivedBatchId: number, batchData: Uint8Array, connection: HubConnection): Promise<void> {
|
||||
if (receivedBatchId < this.nextBatchId) {
|
||||
// SignalR delivers messages in order, but it does not guarantee that the message gets delivered.
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ const browserRenderers: BrowserRendererRegistry = {};
|
|||
let shouldResetScrollAfterNextBatch = false;
|
||||
|
||||
export function attachRootComponentToLogicalElement(browserRendererId: number, logicalElement: LogicalElement, componentId: number): void {
|
||||
|
||||
let browserRenderer = browserRenderers[browserRendererId];
|
||||
if (!browserRenderer) {
|
||||
browserRenderer = browserRenderers[browserRendererId] = new BrowserRenderer(browserRendererId);
|
||||
|
|
@ -21,15 +20,15 @@ export function attachRootComponentToLogicalElement(browserRendererId: number, l
|
|||
browserRenderer.attachRootComponentToLogicalElement(componentId, logicalElement);
|
||||
}
|
||||
|
||||
export function attachRootComponentToElement(browserRendererId: number, elementSelector: string, componentId: number): void {
|
||||
|
||||
export function attachRootComponentToElement(elementSelector: string, componentId: number, browserRendererId?: number): void {
|
||||
const element = document.querySelector(elementSelector);
|
||||
if (!element) {
|
||||
throw new Error(`Could not find any element matching selector '${elementSelector}'.`);
|
||||
}
|
||||
|
||||
// 'allowExistingContents' to keep any prerendered content until we do the first client-side render
|
||||
attachRootComponentToLogicalElement(browserRendererId, toLogicalElement(element, /* allow existing contents */ true), componentId);
|
||||
// Only client-side Blazor supplies a browser renderer ID
|
||||
attachRootComponentToLogicalElement(browserRendererId || 0, toLogicalElement(element, /* allow existing contents */ true), componentId);
|
||||
}
|
||||
|
||||
export function renderBatch(browserRendererId: number, batch: RenderBatch): void {
|
||||
|
|
|
|||
|
|
@ -10,23 +10,8 @@ jest.mock('../src/Rendering/Renderer', () => ({
|
|||
|
||||
describe('RenderQueue', () => {
|
||||
|
||||
it('getOrCreateRenderQueue returns a new queue if one does not exist for a renderer', () => {
|
||||
const queue = RenderQueue.getOrCreateQueue(1, NullLogger.instance);
|
||||
|
||||
expect(queue).toBeDefined();
|
||||
|
||||
});
|
||||
|
||||
it('getOrCreateRenderQueue returns an existing queue if one exists for a renderer', () => {
|
||||
const queue = RenderQueue.getOrCreateQueue(2, NullLogger.instance);
|
||||
const secondQueue = RenderQueue.getOrCreateQueue(2, NullLogger.instance);
|
||||
|
||||
expect(secondQueue).toBe(queue);
|
||||
|
||||
});
|
||||
|
||||
it('processBatch acknowledges previously rendered batches', () => {
|
||||
const queue = RenderQueue.getOrCreateQueue(3, NullLogger.instance);
|
||||
const queue = new RenderQueue(0, NullLogger.instance);
|
||||
|
||||
const sendMock = jest.fn();
|
||||
const connection = { send: sendMock } as any as signalR.HubConnection;
|
||||
|
|
@ -37,7 +22,7 @@ describe('RenderQueue', () => {
|
|||
});
|
||||
|
||||
it('processBatch does not render out of order batches', () => {
|
||||
const queue = RenderQueue.getOrCreateQueue(4, NullLogger.instance);
|
||||
const queue = new RenderQueue(0, NullLogger.instance);
|
||||
|
||||
const sendMock = jest.fn();
|
||||
const connection = { send: sendMock } as any as signalR.HubConnection;
|
||||
|
|
@ -47,7 +32,7 @@ describe('RenderQueue', () => {
|
|||
});
|
||||
|
||||
it('processBatch renders pending batches', () => {
|
||||
const queue = RenderQueue.getOrCreateQueue(5, NullLogger.instance);
|
||||
const queue = new RenderQueue(0, NullLogger.instance);
|
||||
|
||||
const sendMock = jest.fn();
|
||||
const connection = { send: sendMock } as any as signalR.HubConnection;
|
||||
|
|
|
|||
|
|
@ -422,17 +422,12 @@ namespace Microsoft.AspNetCore.Components.Routing
|
|||
}
|
||||
namespace Microsoft.AspNetCore.Components.Web
|
||||
{
|
||||
public static partial class RendererRegistryEventDispatcher
|
||||
public sealed partial class WebEventDescriptor
|
||||
{
|
||||
[Microsoft.JSInterop.JSInvokableAttribute("DispatchEvent")]
|
||||
public static System.Threading.Tasks.Task DispatchEvent(Microsoft.AspNetCore.Components.Web.RendererRegistryEventDispatcher.BrowserEventDescriptor eventDescriptor, string eventArgsJson) { throw null; }
|
||||
public partial class BrowserEventDescriptor
|
||||
{
|
||||
public BrowserEventDescriptor() { }
|
||||
public int BrowserRendererId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||
public string EventArgsType { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||
public Microsoft.AspNetCore.Components.Rendering.EventFieldInfo EventFieldInfo { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||
public ulong EventHandlerId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||
}
|
||||
public WebEventDescriptor() { }
|
||||
public int BrowserRendererId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||
public string EventArgsType { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||
public Microsoft.AspNetCore.Components.Rendering.EventFieldInfo EventFieldInfo { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||
public ulong EventHandlerId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,3 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Blazor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Components.Server, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Components.Server.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
|
|
|
|||
|
|
@ -1,95 +0,0 @@
|
|||
// 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.Components.Rendering;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace Microsoft.AspNetCore.Components.Web
|
||||
{
|
||||
// Provides mechanisms for locating <see cref="Renderer"/> instances
|
||||
// by ID. This is used when receiving incoming events. It also implicitly
|
||||
// roots <see cref="Renderer"/> instances and their associated component instances
|
||||
// so they cannot be GCed while they are still registered for events.
|
||||
|
||||
/// <summary>
|
||||
/// Framework infrastructure, not intended to be used by application code.
|
||||
/// </summary>
|
||||
internal class RendererRegistry
|
||||
{
|
||||
private static AsyncLocal<RendererRegistry> _current;
|
||||
private static readonly RendererRegistry _globalRegistry;
|
||||
|
||||
// By default the registry will be set to a default value. This means that
|
||||
// things will 'just work when running in the browser.
|
||||
//
|
||||
// Running in Server-Side Components - any call into the Circuit will set this value via
|
||||
// the async local. This will ensure that the incoming call can resolve the correct
|
||||
// renderer associated with the user context.
|
||||
static RendererRegistry()
|
||||
{
|
||||
_current = new AsyncLocal<RendererRegistry>();
|
||||
_globalRegistry = new RendererRegistry();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Framework infrastructure, not intended to be used by application code.
|
||||
/// </summary>
|
||||
public static RendererRegistry Current => _current.Value ?? _globalRegistry;
|
||||
|
||||
/// <summary>
|
||||
/// Framework infrastructure, not intended by used by application code.
|
||||
/// </summary>
|
||||
public static void SetCurrentRendererRegistry(RendererRegistry registry)
|
||||
{
|
||||
_current.Value = registry;
|
||||
}
|
||||
|
||||
private int _nextId;
|
||||
private IDictionary<int, Renderer> _renderers = new Dictionary<int, Renderer>();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Framework infrastructure, not intended by used by application code.
|
||||
/// </summary>
|
||||
public int Add(Renderer renderer)
|
||||
{
|
||||
lock (_renderers)
|
||||
{
|
||||
var id = _nextId++;
|
||||
_renderers.Add(id, renderer);
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Framework infrastructure, not intended by used by application code.
|
||||
/// </summary>
|
||||
public Renderer Find(int rendererId)
|
||||
{
|
||||
lock (_renderers)
|
||||
{
|
||||
return _renderers[rendererId];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Framework infrastructure, not intended by used by application code.
|
||||
/// </summary>
|
||||
public bool TryRemove(int rendererId)
|
||||
{
|
||||
lock (_renderers)
|
||||
{
|
||||
if (_renderers.ContainsKey(rendererId))
|
||||
{
|
||||
_renderers.Remove(rendererId);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
// 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.Components.Rendering;
|
||||
|
||||
namespace Microsoft.AspNetCore.Components.Web
|
||||
{
|
||||
/// <summary>
|
||||
/// For framework use only.
|
||||
/// </summary>
|
||||
public sealed class WebEventDescriptor
|
||||
{
|
||||
// We split the incoming event data in two, because we don't know what type
|
||||
// to use when deserializing the args until we've deserialized the descriptor.
|
||||
// This class represents the first half of the parsing process.
|
||||
// It's public only because it's part of the signature of a [JSInvokable] method.
|
||||
|
||||
/// <summary>
|
||||
/// For framework use only.
|
||||
/// </summary>
|
||||
public int BrowserRendererId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// For framework use only.
|
||||
/// </summary>
|
||||
public ulong EventHandlerId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// For framework use only.
|
||||
/// </summary>
|
||||
public string EventArgsType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// For framework use only.
|
||||
/// </summary>
|
||||
public EventFieldInfo EventFieldInfo { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -37,7 +37,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
|
|||
private void CreateDefaultConfiguration()
|
||||
{
|
||||
Client = new BlazorClient() { DefaultLatencyTimeout = DefaultLatencyTimeout };
|
||||
Client.RenderBatchReceived += (id, rendererId, data) => Batches.Add(new Batch(id, rendererId, data));
|
||||
Client.RenderBatchReceived += (id, data) => Batches.Add(new Batch(id, data));
|
||||
Client.OnCircuitError += (error) => Errors.Add(error);
|
||||
|
||||
_ = _serverFixture.RootUri; // this is needed for the side-effects of getting the URI.
|
||||
|
|
@ -267,15 +267,13 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
|
|||
|
||||
private class Batch
|
||||
{
|
||||
public Batch(int id, int rendererId, byte [] data)
|
||||
public Batch(int id, byte [] data)
|
||||
{
|
||||
Id = id;
|
||||
RendererId = rendererId;
|
||||
Data = data;
|
||||
}
|
||||
|
||||
public int Id { get; }
|
||||
public int RendererId { get; }
|
||||
public byte[] Data { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -386,7 +386,30 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
|
|||
null);
|
||||
|
||||
Assert.Contains(
|
||||
(LogLevel.Debug, "DispatchEventFailedToParseEventDescriptor"),
|
||||
(LogLevel.Debug, "DispatchEventFailedToParseEventData"),
|
||||
logEvents);
|
||||
|
||||
await ValidateClientKeepsWorking(Client, batches);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DispatchingEventsWithInvalidEventDescriptor()
|
||||
{
|
||||
// Arrange
|
||||
var (interopCalls, dotNetCompletions, batches) = ConfigureClient();
|
||||
await GoToTestComponent(batches);
|
||||
var sink = _serverFixture.Host.Services.GetRequiredService<TestSink>();
|
||||
var logEvents = new List<(LogLevel logLevel, string)>();
|
||||
sink.MessageLogged += (wc) => logEvents.Add((wc.LogLevel, wc.EventId.Name));
|
||||
|
||||
// Act
|
||||
await Client.HubConnection.InvokeAsync(
|
||||
"DispatchBrowserEvent",
|
||||
"{Invalid:{\"payload}",
|
||||
"{}");
|
||||
|
||||
Assert.Contains(
|
||||
(LogLevel.Debug, "DispatchEventFailedToParseEventData"),
|
||||
logEvents);
|
||||
|
||||
await ValidateClientKeepsWorking(Client, batches);
|
||||
|
|
@ -403,7 +426,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
|
|||
sink.MessageLogged += (wc) => logEvents.Add((wc.LogLevel, wc.EventId.Name));
|
||||
|
||||
// Act
|
||||
var browserDescriptor = new RendererRegistryEventDispatcher.BrowserEventDescriptor()
|
||||
var browserDescriptor = new WebEventDescriptor()
|
||||
{
|
||||
BrowserRendererId = 0,
|
||||
EventHandlerId = 6,
|
||||
|
|
@ -416,7 +439,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
|
|||
"{Invalid:{\"payload}");
|
||||
|
||||
Assert.Contains(
|
||||
(LogLevel.Debug, "DispatchEventFailedToDispatchEvent"),
|
||||
(LogLevel.Debug, "DispatchEventFailedToParseEventData"),
|
||||
logEvents);
|
||||
|
||||
await ValidateClientKeepsWorking(Client, batches);
|
||||
|
|
@ -438,7 +461,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
|
|||
Type = "click",
|
||||
Detail = 1
|
||||
};
|
||||
var browserDescriptor = new RendererRegistryEventDispatcher.BrowserEventDescriptor()
|
||||
var browserDescriptor = new WebEventDescriptor()
|
||||
{
|
||||
BrowserRendererId = 0,
|
||||
EventHandlerId = 1,
|
||||
|
|
@ -458,47 +481,6 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
|
|||
await ValidateClientKeepsWorking(Client, batches);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DispatchingEventThroughJSInterop()
|
||||
{
|
||||
// Arrange
|
||||
var (interopCalls, dotNetCompletions, batches) = ConfigureClient();
|
||||
await GoToTestComponent(batches);
|
||||
var sink = _serverFixture.Host.Services.GetRequiredService<TestSink>();
|
||||
var logEvents = new List<(LogLevel logLevel, string eventIdName)>();
|
||||
sink.MessageLogged += (wc) => logEvents.Add((wc.LogLevel, wc.EventId.Name));
|
||||
|
||||
// Act
|
||||
var mouseEventArgs = new UIMouseEventArgs()
|
||||
{
|
||||
Type = "click",
|
||||
Detail = 1
|
||||
};
|
||||
var browserDescriptor = new RendererRegistryEventDispatcher.BrowserEventDescriptor()
|
||||
{
|
||||
BrowserRendererId = 0,
|
||||
EventHandlerId = 1,
|
||||
EventArgsType = "mouse",
|
||||
};
|
||||
|
||||
var serializerOptions = TestJsonSerializerOptionsProvider.Options;
|
||||
var uiArgs = JsonSerializer.Serialize(mouseEventArgs, serializerOptions);
|
||||
|
||||
await Assert.ThrowsAsync<TaskCanceledException>(() => Client.InvokeDotNetMethod(
|
||||
0,
|
||||
"Microsoft.AspNetCore.Components.Web",
|
||||
"DispatchEvent",
|
||||
null,
|
||||
JsonSerializer.Serialize(new object[] { browserDescriptor, uiArgs }, serializerOptions)));
|
||||
|
||||
Assert.Contains(
|
||||
(LogLevel.Debug, "DispatchEventThroughJSInterop"),
|
||||
logEvents);
|
||||
|
||||
await ValidateClientKeepsWorking(Client, batches);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task EventHandlerThrowsSyncExceptionTerminatesTheCircuit()
|
||||
{
|
||||
|
|
@ -519,7 +501,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
|
|||
"Handler threw an exception" == e.exception.Message);
|
||||
}
|
||||
|
||||
private Task ValidateClientKeepsWorking(BlazorClient Client, List<(int, int, byte[])> batches) =>
|
||||
private Task ValidateClientKeepsWorking(BlazorClient Client, List<(int, byte[])> batches) =>
|
||||
ValidateClientKeepsWorking(Client, () => batches.Count);
|
||||
|
||||
private async Task ValidateClientKeepsWorking(BlazorClient Client, Func<int> countAccessor)
|
||||
|
|
@ -530,7 +512,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
|
|||
Assert.Equal(currentBatches + 1, countAccessor());
|
||||
}
|
||||
|
||||
private async Task GoToTestComponent(List<(int, int, byte[])> batches)
|
||||
private async Task GoToTestComponent(List<(int, byte[])> batches)
|
||||
{
|
||||
var rootUri = _serverFixture.RootUri;
|
||||
Assert.True(await Client.ConnectAsync(new Uri(rootUri, "/subdir"), prerendered: false), "Couldn't connect to the app");
|
||||
|
|
@ -540,12 +522,12 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
|
|||
Assert.Equal(2, batches.Count);
|
||||
}
|
||||
|
||||
private (List<(int id, string identifier, string args)>, List<string>, List<(int, int, byte[])>) ConfigureClient()
|
||||
private (List<(int id, string identifier, string args)>, List<string>, List<(int, byte[])>) ConfigureClient()
|
||||
{
|
||||
var interopCalls = new List<(int, string, string)>();
|
||||
Client.JSInterop += (int arg1, string arg2, string arg3) => interopCalls.Add((arg1, arg2, arg3));
|
||||
var batches = new List<(int, int, byte[])>();
|
||||
Client.RenderBatchReceived += (id, renderer, data) => batches.Add((id, renderer, data));
|
||||
var batches = new List<(int, byte[])>();
|
||||
Client.RenderBatchReceived += (renderer, data) => batches.Add((renderer, data));
|
||||
var endInvokeDotNetCompletions = new List<string>();
|
||||
Client.DotNetInteropCompletion += (completion) => endInvokeDotNetCompletions.Add(completion);
|
||||
return (interopCalls, endInvokeDotNetCompletions, batches);
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
|
|||
_ = _serverFixture.RootUri;
|
||||
|
||||
Client = new BlazorClient() { DefaultLatencyTimeout = DefaultLatencyTimeout };
|
||||
Client.RenderBatchReceived += (rendererId, id, data) => Batches.Add(new Batch(rendererId, id, data));
|
||||
Client.RenderBatchReceived += (id, data) => Batches.Add(new Batch(id, data));
|
||||
|
||||
Sink = _serverFixture.Host.Services.GetRequiredService<TestSink>();
|
||||
Sink.MessageLogged += LogMessages;
|
||||
|
|
@ -101,15 +101,13 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
|
|||
|
||||
private class Batch
|
||||
{
|
||||
public Batch(int rendererId, int id, byte[] data)
|
||||
public Batch(int id, byte[] data)
|
||||
{
|
||||
Id = id;
|
||||
RendererId = rendererId;
|
||||
Data = data;
|
||||
}
|
||||
|
||||
public int Id { get; }
|
||||
public int RendererId { get; }
|
||||
public byte[] Data { get; }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ namespace Ignitor
|
|||
|
||||
public event Action<int, string, string> JSInterop;
|
||||
|
||||
public event Action<int, int, byte[]> RenderBatchReceived;
|
||||
public event Action<int, byte[]> RenderBatchReceived;
|
||||
|
||||
public event Action<string> DotNetInteropCompletion;
|
||||
|
||||
|
|
@ -232,7 +232,7 @@ namespace Ignitor
|
|||
|
||||
HubConnection.On<int, string, string>("JS.BeginInvokeJS", OnBeginInvokeJS);
|
||||
HubConnection.On<string>("JS.EndInvokeDotNet", OnEndInvokeDotNet);
|
||||
HubConnection.On<int, int, byte[]>("JS.RenderBatch", OnRenderBatch);
|
||||
HubConnection.On<int, byte[]>("JS.RenderBatch", OnRenderBatch);
|
||||
HubConnection.On<string>("JS.Error", OnError);
|
||||
HubConnection.Closed += OnClosedAsync;
|
||||
|
||||
|
|
@ -286,11 +286,11 @@ namespace Ignitor
|
|||
}
|
||||
}
|
||||
|
||||
private void OnRenderBatch(int browserRendererId, int batchId, byte[] batchData)
|
||||
private void OnRenderBatch(int batchId, byte[] batchData)
|
||||
{
|
||||
try
|
||||
{
|
||||
RenderBatchReceived?.Invoke(browserRendererId, batchId, batchData);
|
||||
RenderBatchReceived?.Invoke(batchId, batchData);
|
||||
|
||||
var batch = RenderBatchReader.Read(batchData);
|
||||
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ namespace Ignitor
|
|||
Value = value
|
||||
};
|
||||
|
||||
var browserDescriptor = new RendererRegistryEventDispatcher.BrowserEventDescriptor()
|
||||
var webEventDescriptor = new WebEventDescriptor()
|
||||
{
|
||||
BrowserRendererId = 0,
|
||||
EventHandlerId = changeEventDescriptor.EventId,
|
||||
|
|
@ -87,7 +87,7 @@ namespace Ignitor
|
|||
}
|
||||
};
|
||||
|
||||
return DispatchEventCore(connection, Serialize(browserDescriptor), Serialize(args));
|
||||
return DispatchEventCore(connection, Serialize(webEventDescriptor), Serialize(args));
|
||||
}
|
||||
|
||||
public Task ClickAsync(HubConnection connection)
|
||||
|
|
@ -102,14 +102,14 @@ namespace Ignitor
|
|||
Type = clickEventDescriptor.EventName,
|
||||
Detail = 1
|
||||
};
|
||||
var browserDescriptor = new RendererRegistryEventDispatcher.BrowserEventDescriptor()
|
||||
var webEventDescriptor = new WebEventDescriptor
|
||||
{
|
||||
BrowserRendererId = 0,
|
||||
EventHandlerId = clickEventDescriptor.EventId,
|
||||
EventArgsType = "mouse",
|
||||
};
|
||||
|
||||
return DispatchEventCore(connection, Serialize(browserDescriptor), Serialize(mouseEventArgs));
|
||||
return DispatchEventCore(connection, Serialize(webEventDescriptor), Serialize(mouseEventArgs));
|
||||
}
|
||||
|
||||
private static string Serialize<T>(T payload) =>
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ namespace Ignitor
|
|||
var done = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
|
||||
// Click the counter button 1000 times
|
||||
client.RenderBatchReceived += (int browserRendererId, int batchId, byte[] data) =>
|
||||
client.RenderBatchReceived += (int batchId, byte[] data) =>
|
||||
{
|
||||
if (batchId < 1000)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue