Skip to content

Commit bda5f61

Browse files
committed
Merge in 'release/8.0' changes
2 parents 1327853 + b445dd6 commit bda5f61

File tree

11 files changed

+92
-25
lines changed

11 files changed

+92
-25
lines changed

src/Components/Web.JS/dist/Release/blazor.server.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Components/Web.JS/dist/Release/blazor.web.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Components/Web.JS/dist/Release/blazor.webview.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Components/Web.JS/src/Boot.Web.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ function boot(options?: Partial<WebStartOptions>) : Promise<void> {
3838
options = options || {};
3939
options.logLevel ??= LogLevel.Error;
4040
Blazor._internal.loadWebAssemblyQuicklyTimeout = 3000;
41+
Blazor._internal.isBlazorWeb = true;
4142

4243
// Defined here to avoid inadvertently imported enhanced navigation
4344
// related APIs in WebAssembly or Blazor Server contexts.

src/Components/Web.JS/src/GlobalExports.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ export interface IBlazor {
8080
receiveWebViewDotNetDataStream?: (streamId: number, data: any, bytesRead: number, errorMessage: string) => void;
8181
attachWebRendererInterop?: typeof attachWebRendererInterop;
8282
loadWebAssemblyQuicklyTimeout?: number;
83+
isBlazorWeb?: boolean;
8384

8485
// JSExport APIs
8586
dotNetExports?: {

src/Components/Web.JS/src/Services/NavigationManager.ts

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { EventDelegator } from '../Rendering/Events/EventDelegator';
77
import { attachEnhancedNavigationListener, getInteractiveRouterRendererId, handleClickForNavigationInterception, hasInteractiveRouter, hasProgrammaticEnhancedNavigationHandler, isWithinBaseUriSpace, performProgrammaticEnhancedNavigation, setHasInteractiveRouter, toAbsoluteUri } from './NavigationUtils';
88
import { WebRendererId } from '../Rendering/WebRendererId';
99
import { isRendererAttached } from '../Rendering/WebRendererInteropMethods';
10+
import { IBlazor } from '../GlobalExports';
1011

1112
let hasRegisteredNavigationEventListeners = false;
1213
let currentHistoryIndex = 0;
@@ -142,18 +143,21 @@ function navigateToFromDotNet(uri: string, options: NavigationOptions): void {
142143

143144
function navigateToCore(uri: string, options: NavigationOptions, skipLocationChangingCallback = false): void {
144145
const absoluteUri = toAbsoluteUri(uri);
146+
const pageLoadMechanism = currentPageLoadMechanism();
145147

146-
if (!options.forceLoad && isWithinBaseUriSpace(absoluteUri)) {
147-
if (shouldUseClientSideRouting()) {
148-
performInternalNavigation(absoluteUri, false, options.replaceHistoryEntry, options.historyEntryState, skipLocationChangingCallback);
149-
} else {
150-
performProgrammaticEnhancedNavigation(absoluteUri, options.replaceHistoryEntry);
151-
}
152-
} else {
148+
if (options.forceLoad || !isWithinBaseUriSpace(absoluteUri) || pageLoadMechanism === 'serverside-fullpageload') {
153149
// For external navigation, we work in terms of the originally-supplied uri string,
154150
// not the computed absoluteUri. This is in case there are some special URI formats
155151
// we're unable to translate into absolute URIs.
156152
performExternalNavigation(uri, options.replaceHistoryEntry);
153+
} else if (pageLoadMechanism === 'clientside-router') {
154+
performInternalNavigation(absoluteUri, false, options.replaceHistoryEntry, options.historyEntryState, skipLocationChangingCallback);
155+
} else if (pageLoadMechanism === 'serverside-enhanced') {
156+
performProgrammaticEnhancedNavigation(absoluteUri, options.replaceHistoryEntry);
157+
} else {
158+
// Force a compile-time error if some other case needs to be handled in the future
159+
const unreachable: never = pageLoadMechanism;
160+
throw new Error(`Unsupported page load mechanism: ${unreachable}`);
157161
}
158162
}
159163

@@ -287,7 +291,7 @@ async function notifyLocationChanged(interceptedLink: boolean, internalDestinati
287291
}
288292

289293
async function onPopState(state: PopStateEvent) {
290-
if (popStateCallback && shouldUseClientSideRouting()) {
294+
if (popStateCallback && currentPageLoadMechanism() !== 'serverside-enhanced') {
291295
await popStateCallback(state);
292296
}
293297

@@ -303,10 +307,24 @@ function getInteractiveRouterNavigationCallbacks(): NavigationCallbacks | undefi
303307
return navigationCallbacks.get(interactiveRouterRendererId);
304308
}
305309

306-
function shouldUseClientSideRouting() {
307-
return hasInteractiveRouter() || !hasProgrammaticEnhancedNavigationHandler();
310+
function currentPageLoadMechanism(): PageLoadMechanism {
311+
if (hasInteractiveRouter()) {
312+
return 'clientside-router';
313+
} else if (hasProgrammaticEnhancedNavigationHandler()) {
314+
return 'serverside-enhanced';
315+
} else {
316+
// For back-compat, in blazor.server.js or blazor.webassembly.js, we always behave as if there's an interactive
317+
// router even if there isn't one attached. This preserves a niche case where people may call Blazor.navigateTo
318+
// without a router and expect to receive a notification on the .NET side but no page load occurs.
319+
// In blazor.web.js, we explicitly recognize the case where you have neither an interactive nor enhanced SSR router
320+
// attached, and then handle Blazor.navigateTo by doing a full page load because that's more useful (issue #51636).
321+
const isBlazorWeb = (window['Blazor'] as IBlazor)._internal.isBlazorWeb;
322+
return isBlazorWeb ? 'serverside-fullpageload' : 'clientside-router';
323+
}
308324
}
309325

326+
type PageLoadMechanism = 'clientside-router' | 'serverside-enhanced' | 'serverside-fullpageload';
327+
310328
// Keep in sync with Components/src/NavigationOptions.cs
311329
export interface NavigationOptions {
312330
forceLoad: boolean;

src/Components/test/E2ETest/ServerRenderingTests/EnhancedNavigationTest.cs

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -558,15 +558,5 @@ private void AssertEnhancedUpdateCountEquals(long count)
558558
=> Browser.Equal(count, () => ((IJavaScriptExecutor)Browser).ExecuteScript("return window.enhancedPageUpdateCount;"));
559559

560560
private static bool IsElementStale(IWebElement element)
561-
{
562-
try
563-
{
564-
_ = element.Enabled;
565-
return false;
566-
}
567-
catch (StaleElementReferenceException)
568-
{
569-
return true;
570-
}
571-
}
561+
=> EnhancedNavigationTestUtil.IsElementStale(element);
572562
}

src/Components/test/E2ETest/ServerRenderingTests/EnhancedNavigationTestUtil.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,17 @@ public static void SuppressEnhancedNavigation<TServerFixture>(ServerTestBase<TSe
3232

3333
public static long GetScrollY(this IWebDriver browser)
3434
=> Convert.ToInt64(((IJavaScriptExecutor)browser).ExecuteScript("return window.scrollY"), CultureInfo.CurrentCulture);
35+
36+
public static bool IsElementStale(IWebElement element)
37+
{
38+
try
39+
{
40+
_ = element.Enabled;
41+
return false;
42+
}
43+
catch (StaleElementReferenceException)
44+
{
45+
return true;
46+
}
47+
}
3548
}

src/Components/test/E2ETest/ServerRenderingTests/InteractivityTest.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,6 +1110,30 @@ public void CanPersistPrerenderedState_WebAssemblyPrerenderedStateAvailableOnlyO
11101110
Browser.Equal("not restored", () => Browser.FindElement(By.Id("wasm")).Text);
11111111
}
11121112

1113+
[Theory]
1114+
[InlineData(false, false)]
1115+
[InlineData(false, true)]
1116+
[InlineData(true, false)]
1117+
[InlineData(true, true)]
1118+
public void CanPerformNavigateToFromInteractiveEventHandler(bool suppressEnhancedNavigation, bool forceLoad)
1119+
{
1120+
EnhancedNavigationTestUtil.SuppressEnhancedNavigation(this, suppressEnhancedNavigation);
1121+
1122+
// Get to the test page
1123+
Navigate($"{ServerPathBase}/interactivity/navigateto");
1124+
Browser.Equal("Interactive NavigateTo", () => Browser.FindElement(By.TagName("h1")).Text);
1125+
var originalNavElem = Browser.FindElement(By.TagName("nav"));
1126+
1127+
// Perform the navigation
1128+
Browser.Click(By.Id(forceLoad ? "perform-navigateto-force" : "perform-navigateto"));
1129+
Browser.True(() => Browser.Url.EndsWith("/nav", StringComparison.Ordinal));
1130+
Browser.Equal("Hello", () => Browser.FindElement(By.Id("nav-home")).Text);
1131+
1132+
// Verify the elements were preserved if and only if they should be
1133+
var shouldPreserveElements = !suppressEnhancedNavigation && !forceLoad;
1134+
Assert.Equal(shouldPreserveElements, !EnhancedNavigationTestUtil.IsElementStale(originalNavElem));
1135+
}
1136+
11131137
private void BlockWebAssemblyResourceLoad()
11141138
{
11151139
((IJavaScriptExecutor)Browser).ExecuteScript("sessionStorage.setItem('block-load-boot-resource', 'true')");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
@page "/interactivity/navigateto"
2+
@layout Components.TestServer.RazorComponents.Shared.EnhancedNavLayout
3+
@inject NavigationManager Nav
4+
@rendermode RenderMode.InteractiveServer
5+
6+
<h1>Interactive NavigateTo</h1>
7+
8+
<p>Shows that NavigateTo from an interactive event handler works as expected, with or without enhanced navigation.</p>
9+
10+
<button id="perform-navigateto" @onclick="@(() => PerformNavigateTo(false))">Navigate</button>
11+
<button id="perform-navigateto-force" @onclick="@(() => PerformNavigateTo(true))">Navigate (force load)</button>
12+
13+
@code {
14+
void PerformNavigateTo(bool forceLoad)
15+
{
16+
Nav.NavigateTo("nav", forceLoad);
17+
}
18+
}

0 commit comments

Comments
 (0)