Page Navigation Feature
The Page Navigation feature controls loading URLs, HTML strings, and intercepting navigation requests. It is available at build time for the initial page and at runtime for programmatic navigation.
Contents
- Builder Configuration
- Runtime Navigation
- Current URL
- Navigation Interception
- Custom URL Schemes
- Navigation Result
Builder Configuration
Set the initial page content before Build():
var builder = InfiniFrameWindowBuilder.Create()
.SetStartPageUrl("https://example.com") // Load a URL as the initial page
.SetStartPageUrl(new Uri("https://example.com")) // Load a URI as the initial page
.SetStartPageContent("<html><body>Hello</body></html>") // Render HTML directly
| Method | Description |
|---|---|
SetStartPageUrl(string?) | Load a URL string as the initial page |
SetUrl(Uri?) | Load a URI as the initial page |
SetStartPageContent(string?) | Render raw HTML as the initial page |
SetStartPageUrl and SetStartPageContent are mutually exclusive; the last one set wins.
Runtime Navigation
After Build(), navigate to new pages through the feature interface or extension methods:
// Load a URL
window.Features.PageNavigation.Load("https://example.com");
await window.Features.PageNavigation.LoadAsync("https://example.com");
// Load a Uri
window.Features.PageNavigation.Load(new Uri("https://example.com"));
// Load raw HTML
window.Features.PageNavigation.LoadRawString("<h1>Hello</h1>");
await window.Features.PageNavigation.LoadRawStringAsync("<h1>Hello</h1>");
// Extension methods
window.Load("https://example.com");
window.LoadRawString("<h1>Hello</h1>");
Safe navigation
TryLoadUri and TryLoadPath return true if the navigation was initiated successfully, false otherwise:
bool loaded = window.Features.PageNavigation.TryLoadUri(new Uri("https://example.com"));
if (!loaded) {
Console.WriteLine("Navigation failed to start");
}
Current URL
Read the current page URL:
string? url = window.Features.PageNavigation.GetCurrentUrl();
Uri? uri = window.Features.PageNavigation.GetCurrentUri();
// Extension methods
string? url2 = window.GetCurrentUrl();
Uri? uri2 = window.GetCurrentUri();
GetCurrentUrl returns the active top-level URL after any redirects. It is null when the window has loaded raw HTML via LoadRawString because there is no associated URL.
Navigation Interception
Inspect and cancel navigation requests before they are committed by the browser engine:
window.RegisterNavigationStartingHandler((window, args) => {
Console.WriteLine($"Navigation to {args.Url} (userInitiated={args.IsUserInitiated})");
// Block navigations to external origins
if (!args.Url.StartsWith("app://"))
return NavigationStartingResult.Cancel;
return NavigationStartingResult.Allow;
});
NavigationStartingEventArgs
| Property | Type | Description |
|---|---|---|
Url | string | The target URL |
IsUserInitiated | bool | true for link clicks and form submissions |
IsRedirect | bool | true for server redirects |
IsMainFrame | bool | true for main frame navigations |
Platform notes
- Windows (WebView2): Uses
ICoreWebView2NavigationStartingEventArgs.IsMainFrameis alwaystruebecause WebView2'sNavigationStartingEventArgsdoes not expose this flag.IsRedirectmaps to theIsRedirectedproperty. - macOS (WKWebView): Uses
WKNavigationDelegate.decidePolicyForNavigationAction:.IsUserInitiatedistrueforWKNavigationTypeLinkActivatedandWKNavigationTypeFormSubmitted. - Linux (WebKitGTK): Uses the
decide-policysignal.IsMainFrameis alwaystruebecause WebKitGTK'sdecide-policywithWEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTIONonly fires for main frame navigations.
Custom URL Schemes
You can intercept requests for custom URL schemes (e.g. app://) and serve content from C# code. This is useful for loading local assets or implementing a virtual file system.
builder.RegisterCustomSchemeHandler("app", (sender, scheme, url, out string? contentType) => {
contentType = "text/html";
var html = "<html><body>Hello from custom scheme</body></html>";
return new MemoryStream(Encoding.UTF8.GetBytes(html));
});
- Up to 16 custom schemes can be registered before
Build()is called. - Additional handlers can be added after
Build()viawindow.RegisterCustomSchemeHandler(...). - Scheme names are lowercased automatically.
CORS and same-origin policy
Custom scheme responses automatically include CORS headers when the request originates from the same origin (same scheme, host, and port). This allows fetch() and XMLHttpRequest to work without disabling web security.
Same-origin behavior (e.g., app://localhost page fetching app://localhost/data.json):
Access-Control-Allow-Origin: app://localhostAccess-Control-Allow-Credentials: trueVary: Origin
Cross-origin behavior (e.g., https://example.com page fetching app://localhost/data.json):
- No CORS headers are added
- The browser engine may block the request entirely depending on web security settings
Platform notes:
- Windows (WebView2): CORS headers are built via
BuildCustomSchemeResponseHeadersand set on theICoreWebView2WebResourceResponse. Theappscheme is registered withTreatAsSecure(TRUE)andHasAuthorityComponent(TRUE). - Linux (WebKitGTK): The
appscheme is registered as CORS-enabled viawebkit_security_manager_register_uri_scheme_as_cors_enabled(). WebKitGTK handles CORS header injection natively. - macOS (WKWebView): CORS headers are built in the
UrlSchemeHandlerdelegate using the sameIsSameOriginlogic as Windows.
Navigation Result
NavigationResult provides information about the outcome of a navigation operation:
public record NavigationResult(
ulong OperationId,
NavigationStatus Status,
Uri? Uri,
int NativeErrorCode,
string? FailureReason
);
NavigationStatus
| Value | Description |
|---|---|
Succeeded | Navigation completed successfully |
Failed | Navigation failed (check FailureReason) |
Superseded | A new navigation replaced this one before it completed |
WindowClosed | The window was closed during navigation |
See Also
- JavaScript Interop Two-way C#/JS messaging
- Window Features Architecture How the feature system works
- Core Window Guide Builder API and feature overview