Skip to content

Commit 2caaa0e

Browse files
rynowakRyan Nowak
authored andcommitted
PR feedback
1 parent 759b09e commit 2caaa0e

File tree

9 files changed

+152
-86
lines changed

9 files changed

+152
-86
lines changed

src/Components/Blazor/Blazor/ref/Microsoft.AspNetCore.Blazor.netstandard2.0.cs

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4+
namespace Microsoft.AspNetCore.Blazor
5+
{
6+
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
7+
public static partial class JSInteropMethods
8+
{
9+
[Microsoft.JSInterop.JSInvokableAttribute("NotifyLocationChanged")]
10+
public static void NotifyLocationChanged(string uri, bool isInterceptedLink) { }
11+
}
12+
}
413
namespace Microsoft.AspNetCore.Blazor.Hosting
514
{
615
public static partial class BlazorWebAssemblyHost
@@ -68,18 +77,6 @@ protected override void HandleException(System.Exception exception) { }
6877
protected override System.Threading.Tasks.Task UpdateDisplayAsync(in Microsoft.AspNetCore.Components.Rendering.RenderBatch batch) { throw null; }
6978
}
7079
}
71-
namespace Microsoft.AspNetCore.Blazor.Services
72-
{
73-
public partial class WebAssemblyNavigationManager : Microsoft.AspNetCore.Components.NavigationManager
74-
{
75-
internal WebAssemblyNavigationManager() { }
76-
public static readonly Microsoft.AspNetCore.Blazor.Services.WebAssemblyNavigationManager Instance;
77-
protected override void EnsureInitialized() { }
78-
protected override void NavigateToCore(string uri, bool forceLoad) { }
79-
[Microsoft.JSInterop.JSInvokableAttribute("NotifyLocationChanged")]
80-
public static void NotifyLocationChanged(string uri, bool intercepted) { }
81-
}
82-
}
8380
namespace Microsoft.AspNetCore.Components.Builder
8481
{
8582
public static partial class ComponentsApplicationBuilderExtensions
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System.ComponentModel;
5+
using Microsoft.AspNetCore.Blazor.Services;
6+
using Microsoft.JSInterop;
7+
8+
namespace Microsoft.AspNetCore.Blazor
9+
{
10+
/// <summary>
11+
/// Contains methods called by interop. Intended for framework use only, not supported for use in application
12+
/// code.
13+
/// </summary>
14+
[EditorBrowsable(EditorBrowsableState.Never)]
15+
public static class JSInteropMethods
16+
{
17+
/// <summary>
18+
/// For framework use only.
19+
/// </summary>
20+
[JSInvokable(nameof(NotifyLocationChanged))]
21+
public static void NotifyLocationChanged(string uri, bool isInterceptedLink)
22+
{
23+
WebAssemblyNavigationManager.Instance.SetLocation(uri, isInterceptedLink);
24+
}
25+
}
26+
}

src/Components/Blazor/Blazor/src/Services/WebAssemblyNavigationManager.cs

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,14 @@
33

44
using System;
55
using Microsoft.AspNetCore.Components;
6-
using Microsoft.JSInterop;
76
using Interop = Microsoft.AspNetCore.Components.Web.BrowserNavigationManagerInterop;
87

98
namespace Microsoft.AspNetCore.Blazor.Services
109
{
1110
/// <summary>
1211
/// Default client-side implementation of <see cref="NavigationManager"/>.
1312
/// </summary>
14-
public class WebAssemblyNavigationManager : NavigationManager
13+
internal class WebAssemblyNavigationManager : NavigationManager
1514
{
1615
/// <summary>
1716
/// Gets the instance of <see cref="WebAssemblyNavigationManager"/>.
@@ -34,6 +33,12 @@ protected override void EnsureInitialized()
3433
Initialize(baseUri, uri);
3534
}
3635

36+
public void SetLocation(string uri, bool isInterceptedLink)
37+
{
38+
Uri = uri;
39+
NotifyLocationChanged(isInterceptedLink);
40+
}
41+
3742
/// <inheritdoc />
3843
protected override void NavigateToCore(string uri, bool forceLoad)
3944
{
@@ -44,15 +49,5 @@ protected override void NavigateToCore(string uri, bool forceLoad)
4449

4550
WebAssemblyJSRuntime.Instance.Invoke<object>(Interop.NavigateTo, uri, forceLoad);
4651
}
47-
48-
/// <summary>
49-
/// For framework use only.
50-
/// </summary>
51-
[JSInvokable(nameof(NotifyLocationChanged))]
52-
public static void NotifyLocationChanged(string uri, bool intercepted)
53-
{
54-
Instance.Uri = uri;
55-
Instance.NotifyLocationChanged(intercepted);
56-
}
5752
}
5853
}

src/Components/Blazor/Blazor/test/WebAssemblyNavigationManagerTest.cs

Lines changed: 0 additions & 13 deletions
This file was deleted.

src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.cs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -292,19 +292,6 @@ public partial class NavigationException : System.Exception
292292
public NavigationException(string uri) { }
293293
public string Location { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
294294
}
295-
public abstract partial class OwningComponentBase : Microsoft.AspNetCore.Components.ComponentBase, System.IDisposable
296-
{
297-
protected OwningComponentBase() { }
298-
protected bool IsDisposed { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
299-
protected System.IServiceProvider ScopedServices { get { throw null; } }
300-
protected virtual void Dispose(bool disposing) { }
301-
void System.IDisposable.Dispose() { }
302-
}
303-
public abstract partial class OwningComponentBase<TService> : Microsoft.AspNetCore.Components.OwningComponentBase, System.IDisposable
304-
{
305-
protected OwningComponentBase() { }
306-
protected TService Service { get { throw null; } }
307-
}
308295
public abstract partial class NavigationManager
309296
{
310297
protected NavigationManager() { }
@@ -319,6 +306,19 @@ protected void NotifyLocationChanged(bool isInterceptedLink) { }
319306
public System.Uri ToAbsoluteUri(string relativeUri) { throw null; }
320307
public string ToBaseRelativePath(string uri) { throw null; }
321308
}
309+
public abstract partial class OwningComponentBase : Microsoft.AspNetCore.Components.ComponentBase, System.IDisposable
310+
{
311+
protected OwningComponentBase() { }
312+
protected bool IsDisposed { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
313+
protected System.IServiceProvider ScopedServices { get { throw null; } }
314+
protected virtual void Dispose(bool disposing) { }
315+
void System.IDisposable.Dispose() { }
316+
}
317+
public abstract partial class OwningComponentBase<TService> : Microsoft.AspNetCore.Components.OwningComponentBase, System.IDisposable
318+
{
319+
protected OwningComponentBase() { }
320+
protected TService Service { get { throw null; } }
321+
}
322322
public partial class PageDisplay : Microsoft.AspNetCore.Components.IComponent
323323
{
324324
public PageDisplay() { }

src/Components/Components/src/NavigationManager.cs

Lines changed: 55 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
5+
using System.Runtime.InteropServices.ComTypes;
56
using Microsoft.AspNetCore.Components.Routing;
67

78
namespace Microsoft.AspNetCore.Components
@@ -30,11 +31,9 @@ public event EventHandler<LocationChangedEventArgs> LocationChanged
3031

3132
private EventHandler<LocationChangedEventArgs> _locationChanged;
3233

33-
// For the baseUri it's worth storing both the string form and Uri form and
34-
// keeping them in sync. These are always represented as absolute URIs with
35-
// a trailing slash.
34+
// For the baseUri it's worth storing as a System.Uri so we can do operations
35+
// on that type. System.Uri gives us access to the original string anyway.
3636
private Uri _baseUri;
37-
private string _baseUriString;
3837

3938
// The URI. Always represented an absolute URI.
4039
private string _uri;
@@ -53,7 +52,7 @@ public string BaseUri
5352
get
5453
{
5554
AssertInitialized();
56-
return _baseUriString;
55+
return _baseUri.OriginalString;
5756
}
5857
protected set
5958
{
@@ -62,8 +61,7 @@ protected set
6261
value = NormalizeBaseUri(value);
6362
}
6463

65-
_baseUriString = value ?? "/";
66-
_baseUri = new Uri(_baseUriString);
64+
_baseUri = new Uri(value, UriKind.Absolute);
6765
}
6866
}
6967

@@ -82,6 +80,7 @@ public string Uri
8280
}
8381
protected set
8482
{
83+
Validate(_baseUri, value);
8584
_uri = value;
8685
}
8786
}
@@ -125,8 +124,9 @@ protected void Initialize(string baseUri, string uri)
125124

126125
if (_isInitialized)
127126
{
128-
throw new InvalidOperationException($"'{typeof(NavigationManager).Name}' already initialized.");
127+
throw new InvalidOperationException($"'{GetType().Name}' already initialized.");
129128
}
129+
130130
_isInitialized = true;
131131

132132
Uri = uri;
@@ -161,27 +161,27 @@ public Uri ToAbsoluteUri(string relativeUri)
161161
/// <returns>A relative URI path.</returns>
162162
public string ToBaseRelativePath(string uri)
163163
{
164-
if (uri.StartsWith(_baseUriString, StringComparison.Ordinal))
164+
if (uri.StartsWith(_baseUri.OriginalString, StringComparison.Ordinal))
165165
{
166166
// The absolute URI must be of the form "{baseUri}something" (where
167167
// baseUri ends with a slash), and from that we return "something"
168-
return uri.Substring(_baseUriString.Length);
168+
return uri.Substring(_baseUri.OriginalString.Length);
169169
}
170170

171171
var hashIndex = uri.IndexOf('#');
172172
var uriWithoutHash = hashIndex < 0 ? uri : uri.Substring(0, hashIndex);
173-
if ($"{uriWithoutHash}/".Equals(_baseUriString, StringComparison.Ordinal))
173+
if ($"{uriWithoutHash}/".Equals(_baseUri.OriginalString, StringComparison.Ordinal))
174174
{
175175
// Special case: for the base URI "/something/", if you're at
176176
// "/something" then treat it as if you were at "/something/" (i.e.,
177177
// with the trailing slash). It's a bit ambiguous because we don't know
178178
// whether the server would return the same page whether or not the
179179
// slash is present, but ASP.NET Core at least does by default when
180180
// using PathBase.
181-
return uri.Substring(_baseUriString.Length - 1);
181+
return uri.Substring(_baseUri.OriginalString.Length - 1);
182182
}
183183

184-
var message = $"The URI '{uri}' is not contained by the base URI '{_baseUriString}'.";
184+
var message = $"The URI '{uri}' is not contained by the base URI '{_baseUri}'.";
185185
throw new ArgumentException(message);
186186
}
187187

@@ -216,5 +216,47 @@ private void AssertInitialized()
216216
throw new InvalidOperationException($"'{GetType().Name}' has not been initialized.");
217217
}
218218
}
219+
220+
private static bool TryGetLengthOfBaseUriPrefix(Uri baseUri, string uri, out int length)
221+
{
222+
if (uri.StartsWith(baseUri.OriginalString, StringComparison.Ordinal))
223+
{
224+
// The absolute URI must be of the form "{baseUri}something" (where
225+
// baseUri ends with a slash), and from that we return "something"
226+
length = baseUri.OriginalString.Length;
227+
return true;
228+
}
229+
230+
var hashIndex = uri.IndexOf('#');
231+
var uriWithoutHash = hashIndex < 0 ? uri : uri.Substring(0, hashIndex);
232+
if ($"{uriWithoutHash}/".Equals(baseUri.OriginalString, StringComparison.Ordinal))
233+
{
234+
// Special case: for the base URI "/something/", if you're at
235+
// "/something" then treat it as if you were at "/something/" (i.e.,
236+
// with the trailing slash). It's a bit ambiguous because we don't know
237+
// whether the server would return the same page whether or not the
238+
// slash is present, but ASP.NET Core at least does by default when
239+
// using PathBase.
240+
length = baseUri.OriginalString.Length - 1;
241+
return true;
242+
}
243+
244+
length = 0;
245+
return false;
246+
}
247+
248+
private static void Validate(Uri baseUri, string uri)
249+
{
250+
if (baseUri == null || uri == null)
251+
{
252+
return;
253+
}
254+
255+
if (!TryGetLengthOfBaseUriPrefix(baseUri, uri, out _))
256+
{
257+
var message = $"The URI '{uri}' is not contained by the base URI '{baseUri}'.";
258+
throw new ArgumentException(message);
259+
}
260+
}
219261
}
220262
}

src/Components/Components/test/NavigationManagerTest.cs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
5-
using System.Runtime.InteropServices.ComTypes;
65
using Xunit;
76

87
namespace Microsoft.AspNetCore.Components
@@ -43,7 +42,25 @@ public void ComputesCorrectValidBaseRelativePaths(string baseUri, string uri, st
4342
[InlineData("scheme://host/", "otherscheme://host/")]
4443
[InlineData("scheme://host/", "scheme://otherhost/")]
4544
[InlineData("scheme://host/path/", "scheme://host/")]
46-
public void ThrowsForInvalidBaseRelativePaths(string baseUri, string absoluteUri)
45+
public void Uri_ThrowsForInvalidBaseRelativePaths(string baseUri, string absoluteUri)
46+
{
47+
var navigationManager = new TestNavigationManager(baseUri);
48+
49+
var ex = Assert.Throws<ArgumentException>(() =>
50+
{
51+
navigationManager.ToBaseRelativePath(absoluteUri);
52+
});
53+
54+
Assert.Equal(
55+
$"The URI '{absoluteUri}' is not contained by the base URI '{baseUri}'.",
56+
ex.Message);
57+
}
58+
59+
[Theory]
60+
[InlineData("scheme://host/", "otherscheme://host/")]
61+
[InlineData("scheme://host/", "scheme://otherhost/")]
62+
[InlineData("scheme://host/path/", "scheme://host/")]
63+
public void ToBaseRelativePath_ThrowsForInvalidBaseRelativePaths(string baseUri, string absoluteUri)
4764
{
4865
var navigationManager = new TestNavigationManager(baseUri);
4966

0 commit comments

Comments
 (0)