|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Runtime.InteropServices.JavaScript;
using Microsoft.AspNetCore.Components.Routing;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Components.WebAssembly.Services;
/// <summary>
/// Default client-side implementation of <see cref="NavigationManager"/>.
/// </summary>
internal sealed partial class WebAssemblyNavigationManager : NavigationManager
{
private ILogger<WebAssemblyNavigationManager> _logger = default!;
/// <summary>
/// Gets the instance of <see cref="WebAssemblyNavigationManager"/>.
/// </summary>
public static WebAssemblyNavigationManager Instance { get; set; } = default!;
public WebAssemblyNavigationManager(string baseUri, string uri)
{
Initialize(baseUri, uri);
}
public void CreateLogger(ILoggerFactory loggerFactory)
{
if (_logger is not null)
{
throw new InvalidOperationException($"The {nameof(WebAssemblyNavigationManager)} has already created a logger.");
}
_logger = loggerFactory.CreateLogger<WebAssemblyNavigationManager>();
}
public void SetLocation(string uri, string? state, bool isInterceptedLink)
{
Uri = uri;
HistoryEntryState = state;
NotifyLocationChanged(isInterceptedLink);
}
public async ValueTask<bool> HandleLocationChangingAsync(string uri, string? state, bool intercepted)
{
return await NotifyLocationChangingAsync(uri, state, intercepted);
}
/// <inheritdoc />
protected override void NavigateToCore(string uri, NavigationOptions options)
{
ArgumentNullException.ThrowIfNull(uri);
_ = PerformNavigationAsync();
async Task PerformNavigationAsync()
{
try
{
var shouldContinueNavigation = await NotifyLocationChangingAsync(uri, options.HistoryEntryState, false);
if (!shouldContinueNavigation)
{
Log.NavigationCanceled(_logger, uri);
return;
}
NavigationManagerInterop.NavigateTo(uri, options.ForceLoad, options.ReplaceHistoryEntry, options.HistoryEntryState);
}
catch (Exception ex)
{
// We shouldn't ever reach this since exceptions thrown from handlers are handled in HandleLocationChangingHandlerException.
// But if some other exception gets thrown, we still want to know about it.
Log.NavigationFailed(_logger, uri, ex);
}
}
}
/// <inheritdoc />
public override void Refresh(bool forceReload = false)
{
NavigationManagerInterop.Refresh(forceReload);
}
protected override void HandleLocationChangingHandlerException(Exception ex, LocationChangingContext context)
{
Log.NavigationFailed(_logger, context.TargetLocation, ex);
}
protected override void SetNavigationLockState(bool value)
=> InternalJSImportMethods.Instance.NavigationManager_SetHasLocationChangingListeners((int)WebRendererId.WebAssembly, value);
private static partial class Log
{
[LoggerMessage(1, LogLevel.Debug, "Navigation canceled when changing the location to {Uri}", EventName = "NavigationCanceled")]
public static partial void NavigationCanceled(ILogger logger, string uri);
[LoggerMessage(2, LogLevel.Error, "Navigation failed when changing the location to {Uri}", EventName = "NavigationFailed")]
public static partial void NavigationFailed(ILogger logger, string uri, Exception exception);
}
}
internal static partial class NavigationManagerInterop
{
[JSImport(BrowserNavigationManagerInterop.NavigateToWithArgs, "blazor-internal")]
public static partial void NavigateTo(string uri, bool forceLoad, bool replaceHistoryEntry, string? historyEntryState);
[JSImport(BrowserNavigationManagerInterop.Refresh, "blazor-internal")]
public static partial void Refresh(bool forceReload);
}
|