Remove .Web IVTs and refactor. Fixes #12713 (#12967)

* 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:
Steve Sanderson 2019-08-09 00:03:40 +01:00 committed by GitHub
parent 4c49a9206e
commit d716ca9d62
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 290 additions and 383 deletions

View File

@ -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

View File

@ -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>

View File

@ -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;
}
}
}
}

View File

@ -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);
}
}
}

View File

@ -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 />

View File

@ -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);

View File

@ -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,

View File

@ -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)
{

View File

@ -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" />

View File

@ -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)
{
}

View File

@ -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,

View File

@ -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,

View File

@ -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

View File

@ -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));

View File

@ -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);

View File

@ -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.

View File

@ -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 {

View File

@ -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;

View File

@ -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 { } }
}
}

View File

@ -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")]

View File

@ -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;
}
}
}
}
}

View File

@ -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; }
}
}

View File

@ -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; }
}
}

View File

@ -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);

View File

@ -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; }
}

View File

@ -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);

View File

@ -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) =>

View File

@ -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)
{