Window Features Architecture
This guide explains the feature system that underpins every InfiniFrame window. Understanding this architecture helps you navigate the API surface, know which methods are available at build time versus runtime, and find the right feature for any window capability.
Contents
- Overview
- Builder Features vs Runtime Features
- Feature Index
- How Features Are Wired
- Extension Methods
- Custom URL Scheme Handler
- DI Container Integration
Overview
Every window capability in InfiniFrame is encapsulated into a feature. Features are self-contained modules that handle a specific aspect of the window, such as size, state, browser settings, or debugging.
This design gives you:
- Discoverability browse
window.Features.<Name>to find available capabilities - Consistency every feature follows the same builder/runtime pattern
- Fluent API extension methods let you chain configuration on both builder and window
- Testability features are interfaces, so they can be mocked in unit tests
Builder Features vs Runtime Features
Most features have two halves:
Builder Feature (I<Name>InfiniFrameWindowBuilderFeature)
Configures the feature before the window is created. Only available through InfiniFrameWindowBuilder and must be called before Build().
var builder = InfiniFrameWindowBuilder.Create();
builder.Features.Size.SetSize(1280, 720); // Direct feature access
builder.SetSize(1280, 720); // Extension method equivalent
Builder features apply their settings to InfiniFrameNativeParameters, which the native layer reads during window creation. After Build(), builder-only settings cannot be changed.
Runtime Feature (I<Name>InfiniFrameWindowFeature)
Provides getters and setters for a live window. Available through IInfiniFrameWindow.Features.<Name> after Build().
var window = builder.Build();
// Direct feature access
window.Features.Size.SetSize(800, 600);
int width = window.Features.Size.Width;
// Extension method equivalent
window.SetSize(800, 600);
Features with both halves
These features have a builder configuration phase and a runtime mutation phase:
| Feature | Builder | Runtime | Notes |
|---|---|---|---|
| Browser | Settings like user agent, web security, clipboard | Live getters/setters | Some settings are startup-only |
| Debugging | DevTools, remote port, web inspector | Diagnostics, endpoint probe | Remote port is startup-only |
| Decorations | Title, icon, chromeless, transparency | Live title/color changes | Chromeless is startup-only |
| Menu | Initial menu bar | Runtime item manipulation | Full runtime control |
| Notifications | Enable, registration ID | Show notifications | Builder config required |
| Page Navigation | Start URL/HTML | Runtime navigation | Full runtime control |
| Position | Initial location, centering | Runtime move/center | Full runtime control |
| Size | Initial size, min/max, resizable | Runtime resize | Full runtime control |
| State | Initial state (maximized, fullscreen, etc.) | Runtime state changes | Full runtime control |
| Taskbar | (empty pattern consistency) | Progress indicators, flash | Builder exists for pattern consistency |
Builder-only features
These features are configured at build time only and have no runtime interface:
| Feature | What it configures |
|---|---|
| Instance Arbitration | Single-instance enforcement, mutex name |
Runtime-only features
These features have no builder configuration and are only available after Build():
| Feature | What it does |
|---|---|
| Drag Drop | File drop handling and extension filtering |
| File Picker Dialogs | Open/save file and folder dialogs |
| Invoke | Cross-thread dispatch to the window's native thread |
| JavaScript Execution | Execute arbitrary JS in the browser control |
| Lifecycle | Window close, ready wait, teardown |
| Monitors | Query connected display information |
| Web Messaging | Two-way C#/JS messaging |
Feature Index
Every feature is accessed through IInfiniFrameWindow.Features:
window.Features.Size // ISizeInfiniFrameWindowFeature
window.Features.Position // IPositionInfiniFrameWindowFeature
window.Features.State // IStateInfiniFrameWindowFeature
window.Features.Decorations // IDecorationsInfiniFrameWindowFeature
window.Features.Browser // IBrowserInfiniFrameWindowFeature
window.Features.Debugging // IDebuggingInfiniFrameWindowFeature
window.Features.PageNavigation // IPageNavigationInfiniFrameWindowFeature
window.Features.Menu // IMenuInfiniFrameWindowFeature
window.Features.Notifications // INotificationsInfiniFrameWindowFeature
window.Features.Taskbar // ITaskbarInfiniFrameWindowFeature
window.Features.DragDrop // IDragDropInfiniFrameWindowFeature
window.Features.JavaScript // IJavaScriptInfiniFrameWindowFeature
window.Features.Invoke // IInvokeInfiniFrameWindowFeature
window.Features.Lifecycle // ILifecycleInfiniFrameWindowFeature
window.Features.Monitors // IMonitorsInfiniFrameWindowFeature
window.Features.FilePickerDialogs // IFilePickerDialogsInfiniFrameWindowFeature
window.Features.WebMessaging // IWebMessagingInfiniFrameWindowFeature
On the builder, features are accessed through IInfiniFrameWindowBuilder.Features (only features with builder halves are available):
builder.Features.Size // ISizeInfiniFrameWindowBuilderFeature
builder.Features.Position // IPositionInfiniFrameWindowBuilderFeature
builder.Features.State // IStateInfiniFrameWindowBuilderFeature
builder.Features.Decorations // IDecorationsInfiniFrameWindowBuilderFeature
builder.Features.Browser // IBrowserInfiniFrameWindowBuilderFeature
builder.Features.Debugging // IDebuggingInfiniFrameWindowBuilderFeature
builder.Features.PageNavigation // IPageNavigationInfiniFrameWindowBuilderFeature
builder.Features.Menu // IMenuInfiniFrameWindowBuilderFeature
builder.Features.Notifications // INotificationsInfiniFrameWindowBuilderFeature
builder.Features.InstanceArbitration // IInstanceArbitrationInfiniFrameWindowBuilderFeature
How Features Are Wired
-
Builder phase: Each builder feature implements
IInfiniFrameWindowBuilderFeaturewith anApplyToNativeParameters(ref InfiniFrameNativeParameters)method. The builder collects all settings into a native parameters struct. -
Build:
InfiniFrameWindowBuilder.Build()creates the window, thenInfiniFrameWindowFeaturesFactorycreates all runtime feature instances from the DI container, passing the window and the original builder. -
Runtime: Each runtime feature is a live object that wraps the native window handle. Extension methods on
IInfiniFrameWindowdelegate to the appropriate feature.
Extension Methods
Every feature provides fluent extension methods on both IInfiniFrameWindowBuilder and IInfiniFrameWindow. These let you chain configuration without touching Features directly:
// Builder extension methods on IInfiniFrameWindowBuilder
var window = InfiniFrameWindowBuilder.Create()
.SetTitle("My App") // Decorations feature
.SetSize(1280, 720) // Size feature
.Center() // Position feature
.SetMaximized(true) // State feature
.SetDevToolsEnabled(true) // Debugging feature
.SetStartPageUrl("https://app.com") // PageNavigation feature
.Build();
// Runtime extension methods on IInfiniFrameWindow
window.SetSize(800, 600);
window.SetTitle("New Title");
window.SetMaximized(false);
Extension methods are syntactic sugar over direct feature access. Both approaches are equivalent:
// These are identical:
window.SetSize(800, 600);
window.Features.Size.SetSize(800, 600);
Custom URL Scheme Handler
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.
DI Container Integration
When building with a ServiceProvider, the builder reads configuration from the InfiniFrame section automatically:
// appsettings.json
{
"InfiniFrame": {
"Title": "My App",
"Width": 1280,
"Height": 720
}
}
Pass the provider to Build:
var window = builder.Build(serviceProvider);
IInfiniFrameWindow will then be resolvable from the container if registered.