diff --git a/Directory.Packages.props b/Directory.Packages.props
index 4288dc4ce..a81246684 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -36,9 +36,9 @@
-
-
-
-
+
+
+
+
\ No newline at end of file
diff --git a/src/InfiniFrame.Shared/BuilderSnapshots/InfiniFrameWindowEventsSnapshot.cs b/src/InfiniFrame.Shared/BuilderSnapshots/InfiniFrameWindowEventsSnapshot.cs
index 4951b4d8b..e474a4ec5 100644
--- a/src/InfiniFrame.Shared/BuilderSnapshots/InfiniFrameWindowEventsSnapshot.cs
+++ b/src/InfiniFrame.Shared/BuilderSnapshots/InfiniFrameWindowEventsSnapshot.cs
@@ -17,7 +17,7 @@ internal readonly record struct InfiniFrameWindowEventsSnapshot(
Action[] WindowMinimized,
Action[] WebMessageReceived,
Action[] WindowClosingRequested,
- NetClosingDelegate[] WindowClosing,
+ Func[] WindowClosing,
Action[] WindowCreating,
Action[] WindowCreated
);
diff --git a/src/InfiniFrame.Shared/Delegates/NetClosingDelegate.cs b/src/InfiniFrame.Shared/Delegates/NetClosingDelegate.cs
deleted file mode 100644
index cd916461d..000000000
--- a/src/InfiniFrame.Shared/Delegates/NetClosingDelegate.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-// ---------------------------------------------------------------------------------------------------------------------
-// Imports
-// ---------------------------------------------------------------------------------------------------------------------
-namespace InfiniFrame;
-// ---------------------------------------------------------------------------------------------------------------------
-// Code
-// ---------------------------------------------------------------------------------------------------------------------
-public delegate bool NetClosingDelegate(object sender, EventArgs? e);
diff --git a/src/InfiniFrame.Shared/Delegates/NetCustomSchemeDelegate.cs b/src/InfiniFrame.Shared/Delegates/NetCustomSchemeDelegate.cs
index 4f8bec779..a447cc3ef 100644
--- a/src/InfiniFrame.Shared/Delegates/NetCustomSchemeDelegate.cs
+++ b/src/InfiniFrame.Shared/Delegates/NetCustomSchemeDelegate.cs
@@ -5,4 +5,4 @@ namespace InfiniFrame;
// ---------------------------------------------------------------------------------------------------------------------
// Code
// ---------------------------------------------------------------------------------------------------------------------
-public delegate Stream? NetCustomSchemeDelegate(object sender, string scheme, string url, out string? contentType);
+public delegate Stream? NetCustomSchemeDelegate(IInfiniFrameWindow sender, string scheme, string url, out string? contentType);
diff --git a/src/InfiniFrame.Shared/FluentApi/InfiniWindowEventsExtensions.cs b/src/InfiniFrame.Shared/FluentApi/InfiniWindowEventsExtensions.cs
index 643fb96c8..9a2240c06 100644
--- a/src/InfiniFrame.Shared/FluentApi/InfiniWindowEventsExtensions.cs
+++ b/src/InfiniFrame.Shared/FluentApi/InfiniWindowEventsExtensions.cs
@@ -176,11 +176,9 @@ public static T RegisterWindowClosingRequestedHandler(this T builder, Action<
///
/// Returns the current instance.
///
- ///
- ///
- ///
/// The builder to register the handler for.
- public static T RegisterWindowClosingHandler(this T builder, NetClosingDelegate handler) where T : IHasInfiniFrameEvents {
+ /// The handler that will be invoked
+ public static T RegisterWindowClosingHandler(this T builder, Func handler) where T : IHasInfiniFrameEvents {
builder.Events.WindowClosing.Add(handler);
return builder;
}
diff --git a/src/InfiniFrame.Shared/IInfiniFrameWindowEvents.cs b/src/InfiniFrame.Shared/IInfiniFrameWindowEvents.cs
index b80bc6593..a0afb031d 100644
--- a/src/InfiniFrame.Shared/IInfiniFrameWindowEvents.cs
+++ b/src/InfiniFrame.Shared/IInfiniFrameWindowEvents.cs
@@ -9,18 +9,18 @@ namespace InfiniFrame;
// Code
// ---------------------------------------------------------------------------------------------------------------------
public interface IInfiniFrameWindowEvents {
- InfiniFrameOrderedEvent WindowLocationChanged { get; }
- InfiniFrameOrderedEvent WindowSizeChanged { get; }
- InfiniFrameOrderedEvent WindowFocusIn { get; }
- InfiniFrameOrderedEvent WindowMaximized { get; }
- InfiniFrameOrderedEvent WindowRestored { get; }
- InfiniFrameOrderedEvent WindowFocusOut { get; }
- InfiniFrameOrderedEvent WindowMinimized { get; }
- InfiniFrameOrderedEvent WebMessageReceived { get; }
- InfiniFrameOrderedEvent WindowClosingRequested { get; }
- InfiniFrameOrderedClosingEvent WindowClosing { get; }
- InfiniFrameOrderedEvent WindowCreating { get; }
- InfiniFrameOrderedEvent WindowCreated { get; }
+ OrderedEvent WindowLocationChanged { get; }
+ OrderedEvent WindowSizeChanged { get; }
+ OrderedEvent WindowFocusIn { get; }
+ OrderedEvent WindowMaximized { get; }
+ OrderedEvent WindowRestored { get; }
+ OrderedEvent WindowFocusOut { get; }
+ OrderedEvent WindowMinimized { get; }
+ OrderedEvent WebMessageReceived { get; }
+ OrderedEvent WindowClosingRequested { get; }
+ OrderedResultEvent WindowClosing { get; }
+ OrderedEvent WindowCreating { get; }
+ OrderedEvent WindowCreated { get; }
void CompleteSetup(IInfiniFrameWindow sender);
diff --git a/src/InfiniFrame.Shared/Utilities/InfiniFrameOrderedClosingEvent.cs b/src/InfiniFrame.Shared/Utilities/InfiniFrameOrderedClosingEvent.cs
deleted file mode 100644
index 08fbe170e..000000000
--- a/src/InfiniFrame.Shared/Utilities/InfiniFrameOrderedClosingEvent.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-// ---------------------------------------------------------------------------------------------------------------------
-// Imports
-// ---------------------------------------------------------------------------------------------------------------------
-using System.Collections.Immutable;
-
-namespace InfiniFrame.Utilities;
-// ---------------------------------------------------------------------------------------------------------------------
-// Code
-// ---------------------------------------------------------------------------------------------------------------------
-public class InfiniFrameOrderedClosingEvent {
- private ImmutableArray _handlers = ImmutableArray.Empty;
- public ImmutableArray Snapshot => _handlers;
-
- // -----------------------------------------------------------------------------------------------------------------
- // Methods
- // -----------------------------------------------------------------------------------------------------------------
- public void Add(NetClosingDelegate handler) {
- ArgumentNullException.ThrowIfNull(handler);
- ImmutableInterlocked.Update(
- ref _handlers,
- transformer: static (current, item) => current.Add(item),
- handler
- );
- }
-
- public void Remove(NetClosingDelegate handler) {
- ArgumentNullException.ThrowIfNull(handler);
- ImmutableInterlocked.Update(
- ref _handlers,
- transformer: static (current, item) => current.Remove(item),
- handler
- );
- }
-
- public bool? Invoke(IInfiniFrameWindow window) {
- bool? result = null;
- foreach (NetClosingDelegate handler in _handlers) {
- result = handler(window, null);
- }
-
- return result;
- }
-}
diff --git a/src/InfiniFrame.Shared/Utilities/InfiniFrameOrderedEvent.cs b/src/InfiniFrame.Shared/Utilities/OrderedEvent.cs
similarity index 89%
rename from src/InfiniFrame.Shared/Utilities/InfiniFrameOrderedEvent.cs
rename to src/InfiniFrame.Shared/Utilities/OrderedEvent.cs
index 6e7a0b788..9835420eb 100644
--- a/src/InfiniFrame.Shared/Utilities/InfiniFrameOrderedEvent.cs
+++ b/src/InfiniFrame.Shared/Utilities/OrderedEvent.cs
@@ -7,7 +7,7 @@ namespace InfiniFrame.Utilities;
// ---------------------------------------------------------------------------------------------------------------------
// Code
// ---------------------------------------------------------------------------------------------------------------------
-public class InfiniFrameOrderedEvent {
+public class OrderedEvent {
private ImmutableArray> _handlers = ImmutableArray>.Empty;
public ImmutableArray> Snapshot => _handlers;
@@ -39,10 +39,13 @@ public void Invoke(IInfiniFrameWindow window) {
}
}
-public class InfiniFrameOrderedEvent {
+public class OrderedEvent {
private ImmutableArray> _handlers = ImmutableArray>.Empty;
public ImmutableArray> Snapshot => _handlers;
+ // -----------------------------------------------------------------------------------------------------------------
+ // Methods
+ // -----------------------------------------------------------------------------------------------------------------
public void Add(Action handler) {
ArgumentNullException.ThrowIfNull(handler);
ImmutableInterlocked.Update(ref _handlers, transformer: static (current, item) => current.Add(item), handler);
@@ -58,4 +61,4 @@ public void Invoke(IInfiniFrameWindow window, TPayload payload) {
handler(window, payload);
}
}
-}
+}
\ No newline at end of file
diff --git a/src/InfiniFrame.Shared/Utilities/OrderedResultEvent.cs b/src/InfiniFrame.Shared/Utilities/OrderedResultEvent.cs
new file mode 100644
index 000000000..1870cd430
--- /dev/null
+++ b/src/InfiniFrame.Shared/Utilities/OrderedResultEvent.cs
@@ -0,0 +1,42 @@
+// ---------------------------------------------------------------------------------------------------------------------
+// Imports
+// ---------------------------------------------------------------------------------------------------------------------
+using System.Collections.Immutable;
+
+namespace InfiniFrame.Utilities;
+// ---------------------------------------------------------------------------------------------------------------------
+// Code
+// ---------------------------------------------------------------------------------------------------------------------
+public class OrderedResultEvent {
+ private ImmutableArray> _handlers = ImmutableArray>.Empty;
+ public ImmutableArray> Snapshot => _handlers;
+
+ // -----------------------------------------------------------------------------------------------------------------
+ // Methods
+ // -----------------------------------------------------------------------------------------------------------------
+ public void Add(Func handler) {
+ ArgumentNullException.ThrowIfNull(handler);
+ ImmutableInterlocked.Update(ref _handlers, transformer: static (current, item) => current.Add(item), handler);
+ }
+
+ public void Remove(Func handler) {
+ ArgumentNullException.ThrowIfNull(handler);
+ ImmutableInterlocked.Update(ref _handlers, transformer: static (current, item) => current.Remove(item), handler);
+ }
+
+ public TResult?[] Invoke(IInfiniFrameWindow window, TPayload payload) {
+ var results = new TResult?[_handlers.Length];
+ for (int i = 0; i < _handlers.Length; i++) {
+ Func handler = _handlers[i];
+
+ try {
+ results[i] = handler(window, payload);
+ }
+ catch (Exception ex) when (ex is not OperationCanceledException) {
+ results[i] = default;
+ }
+ }
+
+ return results.ToArray();
+ }
+}
diff --git a/src/InfiniFrame/InfiniFrameWindowEvents.cs b/src/InfiniFrame/InfiniFrameWindowEvents.cs
index 2efc4c63c..faf7f802c 100644
--- a/src/InfiniFrame/InfiniFrameWindowEvents.cs
+++ b/src/InfiniFrame/InfiniFrameWindowEvents.cs
@@ -12,18 +12,18 @@ namespace InfiniFrame;
public class InfiniFrameWindowEvents : IInfiniFrameWindowEvents {
private IInfiniFrameWindow Sender { get; set; } = null!;
- public InfiniFrameOrderedEvent WindowLocationChanged { get; } = new();
- public InfiniFrameOrderedEvent WindowSizeChanged { get; } = new();
- public InfiniFrameOrderedEvent WindowFocusIn { get; } = new();
- public InfiniFrameOrderedEvent WindowMaximized { get; } = new();
- public InfiniFrameOrderedEvent WindowRestored { get; } = new();
- public InfiniFrameOrderedEvent WindowFocusOut { get; } = new();
- public InfiniFrameOrderedEvent WindowMinimized { get; } = new();
- public InfiniFrameOrderedEvent WebMessageReceived { get; } = new();
- public InfiniFrameOrderedEvent WindowClosingRequested { get; } = new();
- public InfiniFrameOrderedClosingEvent WindowClosing { get; } = new();
- public InfiniFrameOrderedEvent WindowCreating { get; } = new();
- public InfiniFrameOrderedEvent WindowCreated { get; } = new();
+ public OrderedEvent WindowLocationChanged { get; } = new();
+ public OrderedEvent WindowSizeChanged { get; } = new();
+ public OrderedEvent WindowFocusIn { get; } = new();
+ public OrderedEvent WindowMaximized { get; } = new();
+ public OrderedEvent WindowRestored { get; } = new();
+ public OrderedEvent WindowFocusOut { get; } = new();
+ public OrderedEvent WindowMinimized { get; } = new();
+ public OrderedEvent WebMessageReceived { get; } = new();
+ public OrderedEvent WindowClosingRequested { get; } = new();
+ public OrderedResultEvent WindowClosing { get; } = new();
+ public OrderedEvent WindowCreating { get; } = new();
+ public OrderedEvent WindowCreated { get; } = new();
// -----------------------------------------------------------------------------------------------------------------
// Methods
@@ -103,10 +103,11 @@ public void OnWindowClosingRequested() {
public byte OnWindowClosing() {
//C++ handles bool values as a single byte, C# uses 4 bytes
byte noClose = 0;
- bool? doNotClose = WindowClosing.Invoke(Sender);
- if (doNotClose ?? false)
+ bool[] doNotClose = WindowClosing.Invoke(Sender, null);
+ if (doNotClose.Any(r => r)) {
noClose = 1;
-
+ }
+
return noClose;
}
diff --git a/src/InfiniFrame/StaticAssets/StaticAssetSchemeHandler.cs b/src/InfiniFrame/StaticAssets/StaticAssetSchemeHandler.cs
index dcf73a8e5..86316347c 100644
--- a/src/InfiniFrame/StaticAssets/StaticAssetSchemeHandler.cs
+++ b/src/InfiniFrame/StaticAssets/StaticAssetSchemeHandler.cs
@@ -16,25 +16,24 @@ public static NetCustomSchemeDelegate Create(IFileProvider fileProvider, string
// yes, C# 14 and such have out parameters in their lambas, but we need to support .NET 8.0 which does not natively have this yet
return NetCustomSchemeDelegateWrapper;
- Stream? NetCustomSchemeDelegateWrapper(object sender, string scheme, string url, out string? contentType) {
+ Stream? NetCustomSchemeDelegateWrapper(IInfiniFrameWindow sender, string scheme, string url, out string? contentType) {
#endif
contentType = null;
- if (sender is not IInfiniFrameWindow { Logger: var logger }) return null;
if (!TryGetAssetPath(url, defaultDocument, out string assetPath)) {
- logger.LogDebug("Rejected custom scheme path for {Scheme}: {Url}", scheme, url);
+ sender.Logger.LogDebug("Rejected custom scheme path for {Scheme}: {Url}", scheme, url);
return null;
}
IFileInfo file = fileProvider.GetFileInfo(assetPath);
if (!file.Exists || file.IsDirectory) {
- logger.LogDebug("Custom scheme miss for {Scheme}: {AssetPath} (from {Url})", scheme, assetPath,
+ sender.Logger.LogDebug("Custom scheme miss for {Scheme}: {AssetPath} (from {Url})", scheme, assetPath,
url);
return null;
}
contentType = GetContentType(assetPath);
- logger.LogDebug("Custom scheme hit for {Scheme}: {AssetPath} ({ContentType})", scheme, assetPath,
+ sender.Logger.LogDebug("Custom scheme hit for {Scheme}: {AssetPath} ({ContentType})", scheme, assetPath,
contentType);
return file.CreateReadStream();
diff --git a/tests/InfiniFrameTests.WebServer/InfiniFrameWebApplicationTests.cs b/tests/InfiniFrameTests.WebServer/InfiniFrameWebApplicationTests.cs
index e7dc87194..e2be775e1 100644
--- a/tests/InfiniFrameTests.WebServer/InfiniFrameWebApplicationTests.cs
+++ b/tests/InfiniFrameTests.WebServer/InfiniFrameWebApplicationTests.cs
@@ -124,8 +124,8 @@ public async Task UseAutoServerClose_ClosingHandler_ShouldReturnFalse() {
// Act
app.UseAutoServerClose();
- NetClosingDelegate? capturedHandler = mockEvents.WindowClosing.Snapshot.LastOrDefault();
- bool? result = capturedHandler?.Invoke(new object(), EventArgs.Empty);
+ Func? capturedHandler = mockEvents.WindowClosing.Snapshot.LastOrDefault();
+ bool? result = capturedHandler?.Invoke(mockWindow, EventArgs.Empty);
// Assert
await Assert.That(capturedHandler).IsNotNull();
@@ -152,8 +152,8 @@ public async Task UseAutoServerClose_ClosingHandler_ShouldInitiateStopAsync() {
app.UseAutoServerClose();
// Act
- NetClosingDelegate? capturedHandler = mockEvents.WindowClosing.Snapshot.LastOrDefault();
- capturedHandler?.Invoke(new object(), EventArgs.Empty);
+ Func? capturedHandler = mockEvents.WindowClosing.Snapshot.LastOrDefault();
+ capturedHandler?.Invoke(mockWindow, EventArgs.Empty);
var appLifetime = webApp.Services.GetRequiredService();
DateTime deadline = DateTime.UtcNow.AddSeconds(2);
diff --git a/tests/InfiniFrameTests/InfiniFrameWindowBuilderTests.cs b/tests/InfiniFrameTests/InfiniFrameWindowBuilderTests.cs
index badb06c1a..a0fc39f94 100644
--- a/tests/InfiniFrameTests/InfiniFrameWindowBuilderTests.cs
+++ b/tests/InfiniFrameTests/InfiniFrameWindowBuilderTests.cs
@@ -7,13 +7,14 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
+using NSubstitute;
namespace InfiniFrameTests;
// ---------------------------------------------------------------------------------------------------------------------
// Code
// ---------------------------------------------------------------------------------------------------------------------
public class InfiniFrameWindowBuilderTests {
- private static Stream? EmptyHandler(object sender, string scheme, string url, out string? contentType) {
+ private static Stream? EmptyHandler(IInfiniFrameWindow sender, string scheme, string url, out string? contentType) {
_ = sender;
_ = scheme;
_ = url;
@@ -148,7 +149,7 @@ public async Task CreateSnapshot_ReRegisteringSameScheme_DoesNotMultiplyDelegate
.Where(static item => item.Key == "app")
.Select(static item => item.Value)
.FirstOrDefault();
- copiedHandler?.Invoke(this, "app", "app://resource", out string? _);
+ copiedHandler?.Invoke(Substitute.For(), "app", "app://resource", out string? _);
// Assert
await Assert.That(found).IsTrue();
@@ -156,7 +157,7 @@ public async Task CreateSnapshot_ReRegisteringSameScheme_DoesNotMultiplyDelegate
await Assert.That(callCount).IsEqualTo(2);
return;
- Stream? CountingHandler(object sender, string scheme, string url, out string? contentType) {
+ Stream? CountingHandler(IInfiniFrameWindow sender, string scheme, string url, out string? contentType) {
_ = sender;
_ = scheme;
_ = url;
@@ -186,8 +187,8 @@ public async Task CreateSnapshot_ReRegisteringSameScheme_RemainsStableAcrossRepe
int firstRegisteredCount = first.CustomSchemes.OrderedSchemeNames.Distinct(StringComparer.Ordinal).Count();
int secondRegisteredCount = second.CustomSchemes.OrderedSchemeNames.Distinct(StringComparer.Ordinal).Count();
- firstHandler?.Invoke(this, "app", "app://resource1", out string? _);
- secondHandler?.Invoke(this, "app", "app://resource2", out string? _);
+ firstHandler?.Invoke(Substitute.For(), "app", "app://resource1", out string? _);
+ secondHandler?.Invoke(Substitute.For(), "app", "app://resource2", out string? _);
// Assert
await Assert.That(foundFirst).IsTrue();
diff --git a/tests/InfiniFrameTests/Utilities/OrderedEventTests.cs b/tests/InfiniFrameTests/Utilities/OrderedEventTests.cs
index d157d60ce..0fb38f1fd 100644
--- a/tests/InfiniFrameTests/Utilities/OrderedEventTests.cs
+++ b/tests/InfiniFrameTests/Utilities/OrderedEventTests.cs
@@ -29,7 +29,7 @@ private static InfiniFrameWindow CreateWindow() {
[Test]
public async Task OrderedEvent_InvokesInRegistrationOrder() {
// Arrange
- var orderedEvent = new InfiniFrameOrderedEvent();
+ var orderedEvent = new OrderedEvent();
InfiniFrameWindow window = CreateWindow();
var calls = new List();
@@ -48,7 +48,7 @@ public async Task OrderedEvent_InvokesInRegistrationOrder() {
[Test]
public async Task OrderedEvent_RemoveStopsInvocation() {
// Arrange
- var orderedEvent = new InfiniFrameOrderedEvent();
+ var orderedEvent = new OrderedEvent();
InfiniFrameWindow window = CreateWindow();
var calls = new List();
Action handler = _ => calls.Add(1);
@@ -66,7 +66,7 @@ public async Task OrderedEvent_RemoveStopsInvocation() {
[Test]
public async Task OrderedEvent_SnapshotIsImmutable() {
// Arrange
- var orderedEvent = new InfiniFrameOrderedEvent();
+ var orderedEvent = new OrderedEvent();
Action handler1 = _ => { };
Action handler2 = _ => { };
@@ -84,7 +84,7 @@ public async Task OrderedEvent_SnapshotIsImmutable() {
[Test]
public async Task OrderedEvent_OperatorsAddAndRemove() {
// Arrange
- var orderedEvent = new InfiniFrameOrderedEvent();
+ var orderedEvent = new OrderedEvent();
InfiniFrameWindow window = CreateWindow();
var calls = new List();
Action handler = _ => calls.Add(1);
@@ -102,7 +102,7 @@ public async Task OrderedEvent_OperatorsAddAndRemove() {
[Test]
public async Task OrderedEventWithPayload_InvokesWithPayload() {
// Arrange
- var orderedEvent = new InfiniFrameOrderedEvent();
+ var orderedEvent = new OrderedEvent();
InfiniFrameWindow window = CreateWindow();
var calls = new List();
@@ -121,29 +121,31 @@ public async Task OrderedEventWithPayload_InvokesWithPayload() {
[Test]
public async Task ClosingEvent_ReturnsLastResult() {
// Arrange
- var closingEvent = new InfiniFrameOrderedClosingEvent();
+ var closingEvent = new OrderedResultEvent();
InfiniFrameWindow window = CreateWindow();
closingEvent.Add((_, _) => false);
closingEvent.Add((_, _) => true);
// Act
- bool? result = closingEvent.Invoke(window);
+ bool[] result = closingEvent.Invoke(window, EventArgs.Empty);
// Assert
- await Assert.That(result).IsTrue();
+ await Assert.That(result).Count().IsEqualTo(2);
+ await Assert.That(result.First()).IsFalse();
+ await Assert.That(result.Last()).IsTrue();
}
[Test]
public async Task ClosingEvent_ReturnsNullWhenEmpty() {
// Arrange
- var closingEvent = new InfiniFrameOrderedClosingEvent();
+ var closingEvent = new OrderedResultEvent();
InfiniFrameWindow window = CreateWindow();
// Act
- bool? result = closingEvent.Invoke(window);
+ bool[] result = closingEvent.Invoke(window, EventArgs.Empty);
// Assert
- await Assert.That(result).IsNull();
+ await Assert.That(result).IsEmpty();
}
}