Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow cancellation of navigation events in Blazor #42638

Merged
merged 32 commits into from
Jul 22, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
a7ae3e9
Prototype for canceling internal navigations
MackinnonBuck Jul 5, 2022
05dc74b
New overlapping navigation behavior, changed APIs
MackinnonBuck Jul 7, 2022
c1f8602
Added APIs to block external navigations
MackinnonBuck Jul 8, 2022
0af6785
Updated API baselines, added XML docs.
MackinnonBuck Jul 8, 2022
298117a
Update NavigationManager.cs
MackinnonBuck Jul 8, 2022
08bc4f0
Merge branch 'main' into mbuck/navigation-cancellation
MackinnonBuck Jul 10, 2022
f8bfc12
Safeguard for overlapping navigations
MackinnonBuck Jul 10, 2022
ff6b245
PR feedback
MackinnonBuck Jul 12, 2022
1a7aca5
Cleanup
MackinnonBuck Jul 12, 2022
7086ddd
Create blazor.webview.js
MackinnonBuck Jul 12, 2022
7edfc97
Fixed deadlock, better exception handling
MackinnonBuck Jul 12, 2022
055bedf
Minor cleanup
MackinnonBuck Jul 12, 2022
0cea6cd
BlazorWebView support
MackinnonBuck Jul 13, 2022
804b451
Started E2E tests, fixed Blazor.navigateTo() bug
MackinnonBuck Jul 14, 2022
01c6484
PR feedback
MackinnonBuck Jul 14, 2022
ab6caad
Miscellaneous improvements
MackinnonBuck Jul 14, 2022
bb4d33b
Switch NavigationLock to use IDs
MackinnonBuck Jul 14, 2022
6f449bb
Update RoutingTest.cs
MackinnonBuck Jul 14, 2022
2fca4ce
Fixed history navigation test
MackinnonBuck Jul 15, 2022
3365322
Update NavigationManagerComponent.razor
MackinnonBuck Jul 15, 2022
54d7bca
PR feedback
MackinnonBuck Jul 18, 2022
72230e2
Debugging test failures in CI
MackinnonBuck Jul 18, 2022
4e3cca2
Updated E2E tests
MackinnonBuck Jul 18, 2022
9bfc11e
Fix potential trimming issue
MackinnonBuck Jul 18, 2022
7684aeb
History state entry support, more tests
MackinnonBuck Jul 18, 2022
8e2b8c0
Some PR feedback
MackinnonBuck Jul 19, 2022
cf17ac7
PR feedback + added NavigationManager unit tests
MackinnonBuck Jul 20, 2022
2771923
Updated E2E tests
MackinnonBuck Jul 20, 2022
22997be
Improved exception handling, more unit tests
MackinnonBuck Jul 20, 2022
8c017b8
Update TestCircuitHost.cs
MackinnonBuck Jul 20, 2022
61b99ed
Improved exception handling in handler callbacks
MackinnonBuck Jul 21, 2022
d155810
Fixed/verified Blazor Hybrid functionality
MackinnonBuck Jul 22, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 33 additions & 9 deletions src/Components/Components/src/NavigationManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -298,18 +298,20 @@ protected async ValueTask<bool> NotifyLocationChangingAsync(string uri, string?
return true;
}

_locationChangingCts = new();
var cts = new CancellationTokenSource();

var context = new LocationChangingContext(uri, state, isNavigationIntercepted, _locationChangingCts);
var cancellationToken = _locationChangingCts.Token;
_locationChangingCts = cts;

var cancellationToken = cts.Token;
var context = new LocationChangingContext(uri, state, isNavigationIntercepted, cancellationToken);

try
{
if (handlerCount == 1)
{
var handlerTask = _locationChangingHandlers[0](context);

if (cancellationToken.IsCancellationRequested)
if (context.DidPreventNavigation)
{
return false;
}
Expand All @@ -327,39 +329,61 @@ protected async ValueTask<bool> NotifyLocationChangingAsync(string uri, string?
{
_locationChangingHandlers.CopyTo(locationChangingHandlersCopy);

var locationChangingTasks = new Task[handlerCount];
var locationChangingTasks = new HashSet<Task>();

for (var i = 0; i < handlerCount; i++)
{
var handlerTask = locationChangingHandlersCopy[i](context);

if (cancellationToken.IsCancellationRequested)
if (context.DidPreventNavigation)
{
return false;
}

locationChangingTasks[i] = handlerTask.AsTask();
locationChangingTasks.Add(handlerTask.AsTask());
}

await Task.WhenAll(locationChangingTasks).WaitAsync(cancellationToken);
while (locationChangingTasks.Count != 0)
{
var completedHandler = await Task.WhenAny(locationChangingTasks).WaitAsync(cancellationToken);

if (context.DidPreventNavigation)
{
return false;
}

locationChangingTasks.Remove(completedHandler);
}
}
finally
{
ArrayPool<Func<LocationChangingContext, ValueTask>>.Shared.Return(locationChangingHandlersCopy);
}
}

return !cancellationToken.IsCancellationRequested;
return !context.DidPreventNavigation;
}
catch (TaskCanceledException ex)
{
if (ex.CancellationToken == cancellationToken)
{
// This navigation was in progress when a successive navigation occurred.
// We treat this as a canceled navigation.
return false;
}

throw;
}
finally
{
cts.Cancel();
cts.Dispose();

if (_locationChangingCts == cts)
{
_locationChangingCts = null;
}
}
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,16 @@ namespace Microsoft.AspNetCore.Components.Routing;
/// </summary>
public class LocationChangingContext
{
private readonly CancellationTokenSource _cts;

internal LocationChangingContext(string targetLocation, string? historyEntryState, bool isNavigationIntercepted, CancellationTokenSource cts)
internal LocationChangingContext(string targetLocation, string? historyEntryState, bool isNavigationIntercepted, CancellationToken cancellationToken)
javiercn marked this conversation as resolved.
Show resolved Hide resolved
{
TargetLocation = targetLocation;
HistoryEntryState = historyEntryState;
IsNavigationIntercepted = isNavigationIntercepted;

_cts = cts;
CancellationToken = cancellationToken;
}

internal bool DidPreventNavigation { get; private set; }

/// <summary>
/// Gets the target location.
/// </summary>
Expand All @@ -38,13 +37,13 @@ internal LocationChangingContext(string targetLocation, string? historyEntryStat
/// Gets a <see cref="System.Threading.CancellationToken"/> that can be used to determine if this navigation was canceled
/// (for example, because the user has triggered a different navigation).
/// </summary>
public CancellationToken CancellationToken => _cts.Token;
public CancellationToken CancellationToken { get; }
javiercn marked this conversation as resolved.
Show resolved Hide resolved

/// <summary>
/// Prevents this navigation from continuing.
/// </summary>
public void PreventNavigation()
{
_cts.Cancel();
DidPreventNavigation = true;
}
}
Loading