diff --git a/.github/workflows/shared-release-bump-version.yml b/.github/workflows/shared-release-bump-version.yml index 4cf0d5e64..edac4682c 100644 --- a/.github/workflows/shared-release-bump-version.yml +++ b/.github/workflows/shared-release-bump-version.yml @@ -39,7 +39,7 @@ jobs: - name: Bump Version id: bump - uses: Code-Of-Chaos/action-version-bumper@v2.1.0 + uses: Code-Of-Chaos/action-version-bumper@v2 with: version_file: src/Directory.Build.props additional_version_files: | diff --git a/.github/workflows/shared-release-publish.yml b/.github/workflows/shared-release-publish.yml index 91cbc5e3f..0628e6580 100644 --- a/.github/workflows/shared-release-publish.yml +++ b/.github/workflows/shared-release-publish.yml @@ -112,6 +112,30 @@ jobs: /p:InfiniFrameEnableTestExports=false \ -o ./release + - name: Verify Meta Package Dependencies + shell: bash + run: | + set -euo pipefail + PACKAGE="" + for candidate in ./release/InfiniLore.InfiniFrame.[0-9]*.nupkg; do + if [[ -f "$candidate" && "$candidate" != *.symbols.nupkg ]]; then + PACKAGE="$candidate" + break + fi + done + [ -n "$PACKAGE" ] || { echo "Missing InfiniLore.InfiniFrame meta-package"; exit 1; } + NUSPEC=$(unzip -p "$PACKAGE" 'InfiniLore.InfiniFrame.nuspec') + for dependency in Application Js NativeBridge Security Shared StaticAssets Window; do + echo "$NUSPEC" | grep -q "InfiniLore.InfiniFrame.$dependency" || { + echo "Missing meta-package dependency: InfiniLore.InfiniFrame.$dependency" + exit 1 + } + done + if unzip -l "$PACKAGE" | grep -Eq 'lib/net[^/]*/InfiniFrame\.(Application|Security|StaticAssets|Window)\.dll'; then + echo "The meta-package contains duplicated implementation assemblies" + exit 1 + fi + - name: Upload NuGet Artifacts uses: actions/upload-artifact@v7 with: @@ -173,17 +197,27 @@ jobs: env: NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} run: | - dotnet nuget push "./release/*.nupkg" \ - --api-key "$NUGET_API_KEY" \ - --source https://api.nuget.org/v3/index.json \ - --skip-duplicate + set -euo pipefail + packages=(NativeBridge Shared Js Security StaticAssets Window Application Blazor BlazorWebView WebServer SingleFile InfiniFrame) + for package in "${packages[@]}"; do + prefix="InfiniLore.InfiniFrame.$package" + [[ "$package" == "InfiniFrame" ]] && prefix="InfiniLore.InfiniFrame" + file=$(find ./release -maxdepth 1 -name "$prefix.*.nupkg" ! -name '*.symbols.nupkg' -print -quit) + [ -n "$file" ] || { echo "Missing package: $package"; exit 1; } + dotnet nuget push "$file" --api-key "$NUGET_API_KEY" --source https://api.nuget.org/v3/index.json --skip-duplicate + done - name: Push GitHub Packages if: inputs.publish_to_nuget env: GITHUB_TOKEN: ${{ github.token }} run: | - dotnet nuget push "./release/*.nupkg" \ - --api-key "$GITHUB_TOKEN" \ - --source "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json" \ - --skip-duplicate + set -euo pipefail + packages=(NativeBridge Shared Js Security StaticAssets Window Application Blazor BlazorWebView WebServer SingleFile InfiniFrame) + for package in "${packages[@]}"; do + prefix="InfiniLore.InfiniFrame.$package" + [[ "$package" == "InfiniFrame" ]] && prefix="InfiniLore.InfiniFrame" + file=$(find ./release -maxdepth 1 -name "$prefix.*.nupkg" ! -name '*.symbols.nupkg' -print -quit) + [ -n "$file" ] || { echo "Missing package: $package"; exit 1; } + dotnet nuget push "$file" --api-key "$GITHUB_TOKEN" --source "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json" --skip-duplicate + done diff --git a/.github/workflows/shared-testing-dotnetpack.yml b/.github/workflows/shared-testing-dotnetpack.yml index 0325bc368..914e8149e 100644 --- a/.github/workflows/shared-testing-dotnetpack.yml +++ b/.github/workflows/shared-testing-dotnetpack.yml @@ -82,6 +82,30 @@ jobs: /p:InfiniFrameEnableTestExports=${{ inputs.enable_test_exports }} \ -o ./release + - name: Verify Meta Package Dependencies + shell: bash + run: | + set -euo pipefail + PACKAGE="" + for candidate in ./release/InfiniLore.InfiniFrame.[0-9]*.nupkg; do + if [[ -f "$candidate" && "$candidate" != *.symbols.nupkg ]]; then + PACKAGE="$candidate" + break + fi + done + [ -n "$PACKAGE" ] || { echo "Missing InfiniLore.InfiniFrame meta-package"; exit 1; } + NUSPEC=$(unzip -p "$PACKAGE" 'InfiniLore.InfiniFrame.nuspec') + for dependency in Application Js NativeBridge Security Shared StaticAssets Window; do + echo "$NUSPEC" | grep -q "InfiniLore.InfiniFrame.$dependency" || { + echo "Missing meta-package dependency: InfiniLore.InfiniFrame.$dependency" + exit 1 + } + done + if unzip -l "$PACKAGE" | grep -Eq 'lib/net[^/]*/InfiniFrame\.(Application|Security|StaticAssets|Window)\.dll'; then + echo "The meta-package contains duplicated implementation assemblies" + exit 1 + fi + - name: Verify NativeBridge Package Natives run: | PACKAGE="" diff --git a/.github/workflows/shared-testing-windows.yml b/.github/workflows/shared-testing-windows.yml index 0854df41c..f9241e0dc 100644 --- a/.github/workflows/shared-testing-windows.yml +++ b/.github/workflows/shared-testing-windows.yml @@ -124,7 +124,7 @@ jobs: New-ItemProperty -Path $werBase -Name DumpType -PropertyType DWord -Value 2 -Force | Out-Null New-ItemProperty -Path $werBase -Name DumpCount -PropertyType DWord -Value 20 -Force | Out-Null - foreach ($exe in @("testhost.exe", "InfiniTests.InfiniFrame.exe", "dotnet.exe")) { + foreach ($exe in @("testhost.exe", "InfiniTests.InfiniFrame.Application.exe", "InfiniTests.InfiniFrame.Window.exe", "dotnet.exe")) { $exeKey = Join-Path $werBase $exe New-Item -Path $exeKey -Force | Out-Null New-ItemProperty -Path $exeKey -Name DumpFolder -PropertyType ExpandString -Value $dumpRoot -Force | Out-Null @@ -160,7 +160,7 @@ jobs: Write-Host "Runner architecture: $env:PROCESSOR_ARCHITECTURE" dotnet --info - $nativeFiles = Get-ChildItem -Path "tests/InfiniTests/bin/Release" -Recurse -File ` + $nativeFiles = Get-ChildItem -Path @("tests/InfiniTests.InfiniFrame.Application/bin/Release", "tests/InfiniTests.InfiniFrame.Window/bin/Release") -Recurse -File ` | Where-Object { $_.Name -in @("InfiniFrame.Native.dll", "WebView2Loader.dll", "testhost.exe") } foreach ($file in $nativeFiles) { $machine = Get-PeMachine $file.FullName diff --git a/Directory.Packages.props b/Directory.Packages.props index dc31c4d78..d4df0fa9a 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -5,29 +5,29 @@ - + - + - + - - - - - - - - + + + + + + + + @@ -38,11 +38,11 @@ - - - - - - + + + + + + diff --git a/InfiniFrame.GitHubActions.Release.slnf b/InfiniFrame.GitHubActions.Release.slnf index ca9e1db73..cba6b3c60 100644 --- a/InfiniFrame.GitHubActions.Release.slnf +++ b/InfiniFrame.GitHubActions.Release.slnf @@ -2,14 +2,18 @@ "solution": { "path": "InfiniFrame.slnx", "projects": [ + "src\\InfiniFrame.Application\\InfiniFrame.Application.csproj", "src\\InfiniFrame\\InfiniFrame.csproj", "src\\InfiniFrame.BlazorWebView\\InfiniFrame.BlazorWebView.csproj", "src\\InfiniFrame.Blazor\\InfiniFrame.Blazor.csproj", "src\\InfiniFrame.Js\\InfiniFrame.Js.csproj", "src\\InfiniFrame.NativeBridge\\InfiniFrame.NativeBridge.csproj", + "src\\InfiniFrame.Security\\InfiniFrame.Security.csproj", "src\\InfiniFrame.Shared\\InfiniFrame.Shared.csproj", "src\\InfiniFrame.SingleFile\\InfiniFrame.SingleFile.csproj", - "src\\InfiniFrame.WebServer\\InfiniFrame.WebServer.csproj" + "src\\InfiniFrame.StaticAssets\\InfiniFrame.StaticAssets.csproj", + "src\\InfiniFrame.WebServer\\InfiniFrame.WebServer.csproj", + "src\\InfiniFrame.Window\\InfiniFrame.Window.csproj" ] } } diff --git a/InfiniFrame.GitHubActions.Testing.Automation.slnf b/InfiniFrame.GitHubActions.Testing.Automation.slnf index 7d7f10570..67feb3949 100644 --- a/InfiniFrame.GitHubActions.Testing.Automation.slnf +++ b/InfiniFrame.GitHubActions.Testing.Automation.slnf @@ -2,16 +2,22 @@ "solution": { "path": "InfiniFrame.slnx", "projects": [ + "src\\InfiniFrame.Application\\InfiniFrame.Application.csproj", "src\\InfiniFrame\\InfiniFrame.csproj", "src\\InfiniFrame.BlazorWebView\\InfiniFrame.BlazorWebView.csproj", "src\\InfiniFrame.Blazor\\InfiniFrame.Blazor.csproj", "src\\InfiniFrame.Js\\InfiniFrame.Js.csproj", "src\\InfiniFrame.NativeBridge\\InfiniFrame.NativeBridge.csproj", + "src\\InfiniFrame.Security\\InfiniFrame.Security.csproj", "src\\InfiniFrame.Shared\\InfiniFrame.Shared.csproj", "src\\InfiniFrame.SingleFile\\InfiniFrame.SingleFile.csproj", + "src\\InfiniFrame.StaticAssets\\InfiniFrame.StaticAssets.csproj", "src\\InfiniFrame.WebServer\\InfiniFrame.WebServer.csproj", + "src\\InfiniFrame.Window\\InfiniFrame.Window.csproj", - "tests\\InfiniTests\\InfiniTests.csproj", + "tests\\InfiniTests.TestSupport\\InfiniTests.TestSupport.csproj", + "tests\\InfiniTests.InfiniFrame.Application\\InfiniTests.InfiniFrame.Application.csproj", + "tests\\InfiniTests.InfiniFrame.Window\\InfiniTests.InfiniFrame.Window.csproj", "tests\\InfiniAutomationTests\\InfiniAutomationTests.csproj", "tests\\InfiniAutomationTests.WebApp\\InfiniAutomationTests.WebApp.csproj", "tests\\InfiniAutomationTests.BlazorWebView.MudBlazor\\InfiniAutomationTests.BlazorWebView.MudBlazor.csproj", diff --git a/InfiniFrame.GitHubActions.Testing.slnf b/InfiniFrame.GitHubActions.Testing.slnf index 9fc2bdee5..b761ce09c 100644 --- a/InfiniFrame.GitHubActions.Testing.slnf +++ b/InfiniFrame.GitHubActions.Testing.slnf @@ -2,17 +2,22 @@ "solution": { "path": "InfiniFrame.slnx", "projects": [ + "src\\InfiniFrame.Application\\InfiniFrame.Application.csproj", "src\\InfiniFrame\\InfiniFrame.csproj", "src\\InfiniFrame.BlazorWebView\\InfiniFrame.BlazorWebView.csproj", "src\\InfiniFrame.Blazor\\InfiniFrame.Blazor.csproj", "src\\InfiniFrame.Js\\InfiniFrame.Js.csproj", "src\\InfiniFrame.NativeBridge\\InfiniFrame.NativeBridge.csproj", + "src\\InfiniFrame.Security\\InfiniFrame.Security.csproj", "src\\InfiniFrame.Shared\\InfiniFrame.Shared.csproj", "src\\InfiniFrame.SingleFile\\InfiniFrame.SingleFile.csproj", + "src\\InfiniFrame.StaticAssets\\InfiniFrame.StaticAssets.csproj", "src\\InfiniFrame.WebServer\\InfiniFrame.WebServer.csproj", + "src\\InfiniFrame.Window\\InfiniFrame.Window.csproj", - "tests\\InfiniTests\\InfiniTests.csproj", - "tests\\InfiniTests.InfiniFrame\\InfiniTests.InfiniFrame.csproj", + "tests\\InfiniTests.TestSupport\\InfiniTests.TestSupport.csproj", + "tests\\InfiniTests.InfiniFrame.Application\\InfiniTests.InfiniFrame.Application.csproj", + "tests\\InfiniTests.InfiniFrame.Window\\InfiniTests.InfiniFrame.Window.csproj", "tests\\InfiniTests.InfiniFrame.Blazor\\InfiniTests.InfiniFrame.Blazor.csproj", "tests\\InfiniTests.InfiniFrame.BlazorWebView\\InfiniTests.InfiniFrame.BlazorWebView.csproj", "tests\\InfiniTests.InfiniFrame.Js\\InfiniTests.InfiniFrame.Js.csproj", diff --git a/InfiniFrame.GitHubActions.slnf b/InfiniFrame.GitHubActions.slnf index 28d2bdbc8..8b66ed4d1 100644 --- a/InfiniFrame.GitHubActions.slnf +++ b/InfiniFrame.GitHubActions.slnf @@ -2,17 +2,22 @@ "solution": { "path": "InfiniFrame.slnx", "projects": [ + "src\\InfiniFrame.Application\\InfiniFrame.Application.csproj", "src\\InfiniFrame\\InfiniFrame.csproj", "src\\InfiniFrame.BlazorWebView\\InfiniFrame.BlazorWebView.csproj", "src\\InfiniFrame.Blazor\\InfiniFrame.Blazor.csproj", "src\\InfiniFrame.Js\\InfiniFrame.Js.csproj", "src\\InfiniFrame.NativeBridge\\InfiniFrame.NativeBridge.csproj", + "src\\InfiniFrame.Security\\InfiniFrame.Security.csproj", "src\\InfiniFrame.Shared\\InfiniFrame.Shared.csproj", "src\\InfiniFrame.SingleFile\\InfiniFrame.SingleFile.csproj", + "src\\InfiniFrame.StaticAssets\\InfiniFrame.StaticAssets.csproj", "src\\InfiniFrame.WebServer\\InfiniFrame.WebServer.csproj", + "src\\InfiniFrame.Window\\InfiniFrame.Window.csproj", - "tests\\InfiniTests\\InfiniTests.csproj", - "tests\\InfiniTests.InfiniFrame\\InfiniTests.InfiniFrame.csproj", + "tests\\InfiniTests.TestSupport\\InfiniTests.TestSupport.csproj", + "tests\\InfiniTests.InfiniFrame.Application\\InfiniTests.InfiniFrame.Application.csproj", + "tests\\InfiniTests.InfiniFrame.Window\\InfiniTests.InfiniFrame.Window.csproj", "tests\\InfiniTests.InfiniFrame.Blazor\\InfiniTests.InfiniFrame.Blazor.csproj", "tests\\InfiniTests.InfiniFrame.BlazorWebView\\InfiniTests.InfiniFrame.BlazorWebView.csproj", "tests\\InfiniTests.InfiniFrame.Js\\InfiniTests.InfiniFrame.Js.csproj", diff --git a/InfiniFrame.slnx b/InfiniFrame.slnx index dd65e613e..84ef9785d 100644 --- a/InfiniFrame.slnx +++ b/InfiniFrame.slnx @@ -2,16 +2,12 @@ - - - - @@ -33,6 +29,12 @@ + + + + + + @@ -117,13 +119,17 @@ + + + + @@ -140,8 +146,9 @@ - - + + + diff --git a/README.md b/README.md index 62f93b7b3..62e7c42d2 100644 --- a/README.md +++ b/README.md @@ -54,14 +54,15 @@ Install: `dotnet add package InfiniLore.InfiniFrame.BlazorWebView` ```csharp using InfiniFrame.BlazorWebView; -var builder = InfiniFrameBlazorAppBuilder.CreateDefault(args, w => w - .SetTitle("My Blazor App") - .SetSize(1280, 720) - .Center() -); - -builder.RootComponents.Add("#app"); -builder.RootComponents.Add("head::after"); +var builder = InfiniFrameApplication.CreateBuilder(args); +builder.UseBlazorWebView(config => { + config.ConfigureWindow(w => w + .SetTitle("My Blazor App") + .SetSize(1280, 720) + .Center()); + config.RootComponents.Add("#app"); + config.RootComponents.Add("head::after"); +}); builder.Build().Run(); ``` @@ -71,18 +72,18 @@ builder.Build().Run(); When loading external module origins, explicitly trust them via window builder policy: ```csharp -var app = InfiniFrameBlazorAppBuilder.CreateDefault(windowBuilder: wb => { +var app = InfiniFrameApplication.CreateBuilder().UseBlazorWebView(config => config.ConfigureWindow(wb => { wb.AddTrustedOrigin("https://xyz"); // add redirects too if needed (e.g. cdn.jsdelivr.net, unpkg.com, etc.) -}); +})).Build(); ``` If you must allow every origin (not recommended outside local/dev scenarios): ```csharp -var app = InfiniFrameBlazorAppBuilder.CreateDefault(windowBuilder: wb => { +var app = InfiniFrameApplication.CreateBuilder().UseBlazorWebView(config => config.ConfigureWindow(wb => { wb.SetTrustAllOrigins(true); -}); +})).Build(); ``` ### Host an ASP.NET Core web app @@ -90,11 +91,14 @@ var app = InfiniFrameBlazorAppBuilder.CreateDefault(windowBuilder: wb => { Install: `dotnet add package InfiniLore.InfiniFrame.WebServer` ```csharp +using InfiniFrame.Application; using InfiniFrame.WebServer; -var app = InfiniFrameWebApplication.CreateBuilder(args) - .Build() - .UseAutoServerClose(); +var app = InfiniFrameApplication.CreateBuilder(args) + .WithWindow("web", static window => window.SetTitle("InfiniFrame")) + .UseWebServer("web", web => web.ConfigureWebApplication(application => + application.MapGet("/", () => "Hello from InfiniFrame"))) + .Build(); app.Run(); ``` diff --git a/docs/docs/cpp/native-cpp-api.md b/docs/docs/cpp/native-cpp-api.md index 39da0870b..822225094 100644 --- a/docs/docs/cpp/native-cpp-api.md +++ b/docs/docs/cpp/native-cpp-api.md @@ -1,13 +1,13 @@ # Native C++ API -InfiniFrame native API documentation is maintained directly in Docusaurus and sourced from the native headers and comments under `src/InfiniFrame.Native`. +InfiniFrame native API documentation is maintained directly in Docusaurus and sourced from the native headers and comments under `src/InfiniFrame.NativeBridge/Native`. ## Core headers - `Core/InfiniFrame.h`: top-level native interop include. - `Core/InfiniFrameWindow.h`: main native window class and callbacks. - `Core/InfiniFrameDialog.h`: dialog surface for file/folder/message dialogs. -- `Core/InfiniFrameInitParams.h`: build-time and startup window parameters. +- `Core/InfiniFrameWindowInitParams.h`: build-time and startup window parameters. - `Core/InfiniFrameWindowImpl.h`: shared implementation state for platform windows. ## Shared native types diff --git a/docs/docs/csharp/async-window-contract-design.md b/docs/docs/csharp/async-window-contract-design.md index fa11abd4a..5b21d3342 100644 --- a/docs/docs/csharp/async-window-contract-design.md +++ b/docs/docs/csharp/async-window-contract-design.md @@ -147,7 +147,7 @@ Do not add async getters or async versions of immediate setters. After the prior ## Native ABI additions -All current exports and the size-equality-checked `InfiniFrameInitParams` layout remain unchanged. Additive operations use an opaque managed context, a 64-bit operation ID, and exactly-once completion callbacks. General operations report a terminal result, native code, and a borrowed UTF-8 failure string; file-dialog callbacks report borrowed native strings for the callback duration: +All current exports and the size-equality-checked `InfiniFrameWindowInitParams` layout remain unchanged. Additive operations use an opaque managed context, a 64-bit operation ID, and exactly-once completion callbacks. General operations report a terminal result, native code, and a borrowed UTF-8 failure string; file-dialog callbacks report borrowed native strings for the callback duration: ```cpp using OperationCompletedCallback = diff --git a/docs/docs/guides/blazor-webview.md b/docs/docs/guides/blazor-webview.md index 789914a64..97f8760f4 100644 --- a/docs/docs/guides/blazor-webview.md +++ b/docs/docs/guides/blazor-webview.md @@ -1,275 +1,57 @@ # Blazor WebView Guide -`InfiniLore.InfiniFrame.BlazorWebView` integrates a full Blazor WebAssembly-style application into a native window with no HTTP server required. The Blazor runtime runs entirely in-process. +`InfiniLore.InfiniFrame.BlazorWebView` hosts a Blazor application in an InfiniFrame native window. -## Contents - -- [How It Works](#how-it-works) -- [Project Setup](#project-setup) -- [Program.cs](#programcs) -- [Available Builder API](#available-builder-api) -- [Dependency Injection](#dependency-injection) -- [Custom File Provider](#custom-file-provider) -- [External JS Modules and Trusted Origins](#external-js-modules-and-trusted-origins) -- [Error Handling](#error-handling) -- [HttpClient](#httpclient) -- [Lifecycle](#lifecycle) -- [Custom Window Chrome](#custom-window-chrome) - -## How It Works - -InfiniFrame serves Blazor resources from an internal origin (`app://localhost/`) and handles requests inside the native host. Blazor component files, JavaScript, and CSS are served from an `IFileProvider` backed by your `wwwroot/` folder. -There is no external ASP.NET server required; all communication happens through the native browser bridge. - -`app://localhost/` follows normal browser URL semantics. Query strings and fragments remain in the browser-visible URL, while InfiniFrame removes them only when looking up an embedded resource. For example, navigating to `app://localhost/index.html?mode=desktop#settings` serves `index.html`, leaves the full URL visible, and exposes `#settings` through `window.location.hash`. - -Same-origin browser requests to application assets are supported with both `fetch()` and `XMLHttpRequest`. The native engines register `app` as a secure, authority-bearing scheme and allow the `app://localhost` origin. Cross-origin access is not enabled implicitly; add trusted origins through the URI security policy only when the application genuinely needs them. - -Platform notes: -- Windows uses WebView2 and requires custom-scheme registration support (`ICoreWebView2EnvironmentOptions4`) to allow top-level `app://localhost/...` navigation. -- Linux and macOS use WebKit-based engines and do not depend on WebView2. -- On Windows, if the WebView2 runtime does not support custom-scheme registration, startup fails fast with a clear error asking for a WebView2 runtime update. - -## Project Setup - -Your project must use the Razor SDK: - -```xml - - - net9.0 - Exe - - - - - - -``` - -### wwwroot/index.html - -A minimal host page: - -```html - - - - - - - - - -
Loading...
- - - - -``` - -## Program.cs +## Setup ```csharp +using InfiniFrame.Application; using InfiniFrame.BlazorWebView; -using Microsoft.Extensions.DependencyInjection; - -var builder = InfiniFrameBlazorAppBuilder.CreateDefault(args, w => w - .SetTitle("My Blazor App") - .SetSize(1280, 720) - .Center() - .SetChromeless(true) // Optional: remove native title bar -); - -// Register services (same as a standard Blazor or ASP.NET Core app) -builder.Services.AddSingleton(); -builder.Services.AddScoped(); - -// Register root components (these map to elements in index.html) -builder.RootComponents.Add("#app"); -builder.RootComponents.Add("head::after"); - -builder.Build().Run(); -``` - -`Run()` blocks until the window is closed and then disposes all services. - -## Available Builder API - -`InfiniFrameBlazorAppBuilder` exposes three properties for configuration: - -| Property | Type | Description | -|------------------|-----------------------------|------------------------------------------------------------------------------| -| `WindowBuilder` | `IInfiniFrameWindowBuilder` | Fluent window configuration; all options from the generated C# API reference | -| `Services` | `IServiceCollection` | Standard .NET DI container | -| `RootComponents` | `RootComponentList` | Maps Blazor components to CSS selectors in index.html | - -### Configuring the window separately - -```csharp -var builder = InfiniFrameBlazorAppBuilder.CreateDefault(); - -builder.WithInfiniFrameWindowBuilder(w => w - .SetTitle("Configured Later") - .SetDevToolsEnabled(true) -); -``` - -## Dependency Injection - -The following services are automatically registered and available for injection: - -| Service | Lifetime | Description | -|----------------------|-----------|---------------------------------------| -| `IInfiniFrameWindow` | Singleton | The native window instance | -| `IInfiniFrameJs` | Scoped | JavaScript interop utilities | -| `HttpClient` | Scoped | Preconfigured for in-process requests | -| `Dispatcher` | Singleton | Blazor's component dispatcher | - -### Injecting the window in a component - -```razor -@inject IInfiniFrameWindow Window - - - - -@code { - void Minimize() => Window.Invoke(() => { /* window ops must be on UI thread */ }); - void Close() => Window.Close(); -} -``` - -## Custom File Provider - -By default, files are served from `{AppBaseDirectory}/wwwroot/`. -You can supply a custom `IFileProvider` for embedded resources, encrypted assets, or virtual file systems: - -```csharp -using Microsoft.Extensions.FileProviders; -var embeddedProvider = new EmbeddedFileProvider(typeof(Program).Assembly, "MyApp.wwwroot"); +var builder = InfiniFrameApplication.CreateBuilder(args) + .WithWindow(window => window + .SetTitle("My Blazor App") + .SetSize(1280, 720)); -var builder = InfiniFrameBlazorAppBuilder.CreateDefault( - fileProvider: embeddedProvider, - args: args -); -``` - -## External JS Modules and Trusted Origins - -If your app imports scripts from external origins (for example `import ... from "https://cdn.example/..."`), keep `WebSecurity` enabled and explicitly trust those origins. - -```csharp -var app = InfiniFrameBlazorAppBuilder.CreateDefault(windowBuilder: wb => { - wb.AddTrustedOrigin("https://xyz"); - // add redirects too if needed (e.g. cdn.jsdelivr.net, unpkg.com, etc.) +builder.UseBlazorWebView(configuration => { + configuration.RootComponents.Add("app"); }); -``` - -For multiple hosts: -```csharp -var app = InfiniFrameBlazorAppBuilder.CreateDefault(windowBuilder: wb => { - wb.AddTrustedOrigin("https://xyz"); - wb.AddTrustedOrigin("https://cdn.jsdelivr.net"); - wb.AddTrustedOrigin("https://unpkg.com"); -}); +await using InfiniFrameApplication app = builder.Build(); +await app.RunAsync(); ``` -To disable origin checks entirely (not recommended for production), opt in explicitly: +Configure window features before registering the integration with `WithWindow` or `WithWindow(id, ...)`. +`ConfigureWindow` remains available for integration-specific configuration. ```csharp -var app = InfiniFrameBlazorAppBuilder.CreateDefault(windowBuilder: wb => { - wb.SetTrustAllOrigins(true); +builder.WithWindow(window => window.SetChromeless(true)); +builder.UseBlazorWebView(configuration => { + configuration.ConfigureWindow(window => window.SetDevToolsEnabled(true)); + configuration.RootComponents.Add("app"); }); ``` -Do not use `.SetWebSecurityEnabled(false)` as a workaround for this scenario. +The current manager supports one target window per Blazor WebView integration. Use separate application integrations when independent window managers are required. -## Error Handling +## Services And Components -Unhandled exceptions in the process are caught automatically and shown as a native message dialog: - -``` -Fatal exception -System.NullReferenceException: Object reference not set... -``` - -To customize error handling, register a handler before `Build()`: +Register application services on `builder.Services` and root components on the integration configuration: ```csharp -AppDomain.CurrentDomain.UnhandledException += (_, e) => { - // Custom logging or reporting -}; +builder.Services.AddSingleton(); +builder.UseBlazorWebView(configuration => { + configuration.RootComponents.Add("app"); + configuration.RootComponents.Add("head::after"); +}); ``` -## HttpClient - -An `HttpClient` is registered automatically with `BaseAddress` set to the internal app base URI. -This lets you make in-process requests to your static assets or call external APIs: +`IInfiniFrameWindow`, `IInfiniFrameJs`, `HttpClient`, and the Blazor dispatcher are registered by the integration where applicable. -```razor -@inject HttpClient Http +## File Providers And Security -@code { - protected override async Task OnInitializedAsync() { - var data = await Http.GetFromJsonAsync("data/mydata.json"); - } -} -``` +The integration serves static assets from the application `wwwroot` and generated static-web-assets locations. Use the configuration APIs for custom file providers and explicitly trust external origins when needed. Keep WebSecurity enabled unless disabling it is an intentional development-only decision. ## Lifecycle -``` -InfiniFrameBlazorAppBuilder.CreateDefault() - ↓ -Configure Services & RootComponents - ↓ -.Build() ← Registers the custom scheme, creates the window - ↓ -.Run() ← Starts the Blazor runtime, blocks until window closes - ↓ -DisposeAsync() ← Disposes all services -``` - -## Debugging workflow - -Use devtools and remote debugging separately: - -```csharp -var builder = InfiniFrameBlazorAppBuilder.CreateDefault(args, w => w - .SetDevToolsEnabled(true) - .SetWebInspectorEnabled(true) // macOS 13.3+ Safari Web Inspector attachability - .SetRemoteDebuggingPort(9222) // Windows and Linux, startup-only -); - -var app = builder.Build(); - -if (app.Window.Debug.TryGetRemoteDebuggingEndpoint(out Uri? endpoint)) - Console.WriteLine($"Remote debug endpoint: {endpoint}"); - -app.Run(); -``` - -- `SetDevToolsEnabled(true)` controls local inspector UI. -- `SetWebInspectorEnabled(true)` controls WKWebView Safari Web Inspector attachability on macOS 13.3+. -- `SetRemoteDebuggingPort(int? port)` controls TCP endpoint availability (`1..65535`, `0/null` disables). -- Linux inspector endpoint uses WebKitGTK inspector server (`http://127.0.0.1:/`). -- On Linux, WebKit requires developer extras for remote inspector and keeps them enabled while remote debugging is active. -- On Linux, inspector server configuration is process-scoped (shared across windows in the same process). -- On unsupported platforms (macOS), enabling remote debugging throws `PlatformNotSupportedException`. -- On unsupported platforms (Windows/Linux, or macOS below 13.3), enabling web inspector mode throws `PlatformNotSupportedException`. - -## Custom Window Chrome - -Combine with `InfiniLore.InfiniFrame.Blazor` for a fully custom title bar. - -See the [Custom Window Chrome Guide](custom-window-chrome.md) for details. For JavaScript interop and built-in message handlers, see the [JavaScript Interop Guide](javascript-interop.md). - -## Examples - -- `InfiniFrameExample.BlazorWebView` (`examples/InfiniFrameExample.BlazorWebView`) - minimal Blazor app with window configuration and Serilog -- `InfiniFrameExample.BlazorWebView.MultiWindowSample` (`examples/InfiniFrameExample.BlazorWebView.MultiWindowSample`) - multiple windows each hosting a different Blazor component +`Run` or `RunAsync` starts the native application and the Blazor WebView. Closing the application disposes the WebView manager, message pump, windows, and registered services. diff --git a/docs/docs/guides/core-window.md b/docs/docs/guides/core-window.md index 239d47e54..2c4436a6a 100644 --- a/docs/docs/guides/core-window.md +++ b/docs/docs/guides/core-window.md @@ -33,7 +33,7 @@ The returned `IInfiniFrameWindow` gives you full control over the window at runt ## Single-File Native Packaging -When your app is published as a single-file executable with embedded InfiniFrame native binaries, call `InfiniFrameSingleFileBootstrap.Initialize()` before creating any windows. +When your app is published as a single-file executable with embedded InfiniFrame native binaries, call `InfiniFrameSingleFile.Initialize()` before creating any windows. ```csharp using InfiniFrame; @@ -41,7 +41,7 @@ using InfiniFrame; public static class Program { [STAThread] public static void Main(string[] args) { - InfiniFrameSingleFileBootstrap.Initialize(); + InfiniFrameSingleFile.Initialize(); var window = InfiniFrameWindowBuilder.Create() .SetTitle("My App") diff --git a/docs/docs/guides/getting-started.md b/docs/docs/guides/getting-started.md index b58ce4ce2..4655d6677 100644 --- a/docs/docs/guides/getting-started.md +++ b/docs/docs/guides/getting-started.md @@ -74,7 +74,7 @@ using InfiniFrame; public static class Program { [STAThread] public static void Main(string[] args) { - InfiniFrameSingleFileBootstrap.Initialize(); + InfiniFrameSingleFile.Initialize(); var window = InfiniFrameWindowBuilder.Create() .SetTitle("Hello, InfiniFrame") @@ -124,21 +124,24 @@ A minimal `index.html`: ### Program.cs ```csharp +using InfiniFrame.Application; using InfiniFrame.BlazorWebView; using Microsoft.Extensions.DependencyInjection; -var builder = InfiniFrameBlazorAppBuilder.CreateDefault(args, w => w - .SetTitle("My Blazor App") - .SetSize(1280, 720) - .Center() -); +var builder = InfiniFrameApplication.CreateBuilder(args) + .WithWindow(window => window + .SetTitle("My Blazor App") + .SetSize(1280, 720) + .Center()); // Register your services builder.Services.AddSingleton(); // Register root Blazor components -builder.RootComponents.Add("#app"); -builder.RootComponents.Add("head::after"); +builder.UseBlazorWebView(configuration => { + configuration.RootComponents.Add("app"); + configuration.RootComponents.Add("head::after"); +}); builder.Build().Run(); ``` @@ -166,21 +169,19 @@ dotnet add package InfiniLore.InfiniFrame.WebServer ### Program.cs ```csharp +using InfiniFrame.Application; using InfiniFrame.WebServer; -var app = InfiniFrameWebApplication.CreateBuilder(args) - .Build() - .UseAutoServerClose(); - -// Configure the ASP.NET Core pipeline on app.WebApp -app.WebApp.UseRouting(); -app.WebApp.MapGet("/", () => "Hello from InfiniFrame"); +var app = InfiniFrameApplication.CreateBuilder(args) + .WithWindow("web", static _ => { }) + .UseWebServer("web", web => web.ConfigureWebApplication(application => + application.MapGet("/", () => "Hello from InfiniFrame"))) + .Build(); app.Run(); ``` -The start URL is automatically read from `ASPNETCORE_URLS` or the `urls` configuration key. -`UseAutoServerClose()` ensures the server shuts down gracefully when the window is closed. +The start URL is resolved from the server's bound address after startup, and the server shuts down with the InfiniFrame application. ## Next Steps diff --git a/docs/docs/guides/pack-tool.md b/docs/docs/guides/pack-tool.md index 2fe4ef038..844c30095 100644 --- a/docs/docs/guides/pack-tool.md +++ b/docs/docs/guides/pack-tool.md @@ -30,7 +30,7 @@ Compared to a regular `dotnet publish`, the target additionally: - Removes unpacked sidecar files from the final publish directory - Performs a two-pass publish to ensure all content is available before embedding -Because native files are embedded as resources, your app must initialize the runtime resolver at startup with `InfiniFrameSingleFileBootstrap.Initialize()`. +Because native files are embedded as resources, your app must initialize the runtime resolver at startup with `InfiniFrameSingleFile.Initialize()`. ## How It Works @@ -147,7 +147,7 @@ using InfiniFrame; public static class Program { [STAThread] public static void Main(string[] args) { - InfiniFrameSingleFileBootstrap.Initialize(); + InfiniFrameSingleFile.Initialize(); var window = InfiniFrameWindowBuilder.Create() .SetTitle("My App") @@ -163,7 +163,7 @@ public static class Program { Why this is required: - The publish target embeds `InfiniFrame.Native` and platform loader files (`WebView2Loader.dll` on Windows) as resources. -- `InfiniFrameSingleFileBootstrap.Initialize()` extracts them to a temporary RID-specific folder and registers a native resolver so P/Invoke can load them. +- `InfiniFrameSingleFile.Initialize()` extracts them to a temporary RID-specific folder and registers a native resolver so P/Invoke can load them. Alternatively, use the higher-level `InfiniFrameSingleFile.Initialize()` helper which also configures embedded static web assets for Blazor apps: diff --git a/docs/docs/guides/trim-aot-compatibility.md b/docs/docs/guides/trim-aot-compatibility.md index 4ec08e727..abf018a84 100644 --- a/docs/docs/guides/trim-aot-compatibility.md +++ b/docs/docs/guides/trim-aot-compatibility.md @@ -6,9 +6,10 @@ InfiniFrame includes CI validation lanes for trimming and NativeAOT compatibilit - The public APIs that rely on runtime reflection or dynamic code generation are explicitly annotated with `RequiresUnreferencedCode` and/or `RequiresDynamicCode`. - Trim/AOT compatibility checks run in CI and must pass before release workflows continue. -- `InfiniFrame.SingleFile` is validated with a NativeAOT smoke publish using: +- The core `InfiniFrame` package is validated with a NativeAOT smoke publish using: - `PublishTrimmed=true` - `PublishAot=true` +- The `InfiniFrame.SingleFile` package and MSBuild targets are validated separately by package/target tests; they are not included in the consumer smoke publish. ## Consumer Guidance @@ -19,4 +20,4 @@ InfiniFrame includes CI validation lanes for trimming and NativeAOT compatibilit dotnet publish -c Release -r -p:PublishTrimmed=true -p:PublishAot=true ``` -- If your app uses framework features that depend on reflection (configuration binding, runtime component activation, etc.), account for their requirements explicitly in your trimming strategy. \ No newline at end of file +- If your app uses framework features that depend on reflection (configuration binding, runtime component activation, etc.), account for their requirements explicitly in your trimming strategy. diff --git a/docs/docs/guides/web-server.md b/docs/docs/guides/web-server.md index a8917b614..ca7c64481 100644 --- a/docs/docs/guides/web-server.md +++ b/docs/docs/guides/web-server.md @@ -1,28 +1,6 @@ # Web Server Guide -`InfiniLore.InfiniFrame.WebServer` runs a standard ASP.NET Core web application in a background thread while opening a native window pointed at it. -This approach gives you the full ASP.NET Core pipeline (middleware, controllers, SignalR, minimal APIs, Blazor Server) without any browser overhead. - -For cross-thread dispatch from ASP.NET Core to the window thread, see the [Invoke feature](invoke-feature.md). For an overview of the feature system, see [Window Features Architecture](window-features-architecture.md). - -## Contents - -- [How It Works](#how-it-works) -- [Installation](#installation) -- [Minimal Setup](#minimal-setup) -- [Builder API](#builder-api) -- [Start URL](#start-url) -- [Accessing the Window from ASP.NET Core](#accessing-the-window-from-aspnet-core) -- [Graceful Shutdown](#graceful-shutdown) -- [Example: Blazor Server](#example-blazor-server) -- [Static Web Assets](#static-web-assets) -- [Thread Model](#thread-model) - -## How It Works - -- The ASP.NET Core server starts on a background thread. -- A native window opens and navigates to the server's URL. -- Both shut down together when the window is closed (with `UseAutoServerClose()`). +`InfiniLore.InfiniFrame.WebServer` hosts an ASP.NET Core application alongside native InfiniFrame windows. ## Installation @@ -33,168 +11,71 @@ dotnet add package InfiniLore.InfiniFrame.WebServer ## Minimal Setup ```csharp +using InfiniFrame.Application; using InfiniFrame.WebServer; -var app = InfiniFrameWebApplication.CreateBuilder(args) - .Build() - .UseAutoServerClose(); - -app.WebApp.MapGet("/", () => "Hello from InfiniFrame"); +var app = InfiniFrameApplication.CreateBuilder(args) + .WithWindow("web", window => window.SetTitle("My Desktop App")) + .UseWebServer("web", web => web.ConfigureWebApplication(application => + application.MapGet("/", () => "Hello from InfiniFrame"))) + .Build(); app.Run(); ``` -`app.Run()` starts the web server in the background, opens the window, and blocks until the window is closed. - -## Builder API +`Run()` starts the server, opens the window, and stops the server during application shutdown. -`InfiniFrameWebApplication.CreateBuilder(args)` returns an `InfiniFrameWebApplicationBuilder` with two properties: +## Configuration -| Property | Type | Description | -|----------|----------------------------|----------------------------------------------------------------------| -| `WebApp` | `WebApplicationBuilder` | Standard ASP.NET Core builder; add services, configure Kestrel, etc. | -| `Window` | `InfiniFrameWindowBuilder` | Fluent window configuration | - -### Configuring the window +`InfiniFrameWebServerConfiguration` exposes `WebHost` for Kestrel configuration and +`ConfigureWebApplication` for configuring the built `WebApplication`: ```csharp -var builder = InfiniFrameWebApplication.CreateBuilder(args); - -builder.Window - .SetTitle("My Desktop App") - .SetSize(1280, 720) - .Center() - .SetDevToolsEnabled(true) - .SetRemoteDebuggingPort(9222); // Windows and Linux - -builder.WebApp.Services.AddControllers(); -builder.WebApp.Services.AddSignalR(); - -var app = builder.Build().UseAutoServerClose(); -app.WebApp.MapControllers(); -app.WebApp.MapHub("/hub"); - -app.Run(); -``` - -Remote debugging notes: -- `SetRemoteDebuggingPort(...)` is startup-only and validates `1..65535` (`0/null` disables). -- Linux and Windows are supported. On unsupported platforms (macOS), enabling it throws `PlatformNotSupportedException`. -- Use `app.Window.Debug.TryGetRemoteDebuggingEndpoint(out Uri? endpoint)` after startup to retrieve the endpoint when available. - -## Start URL - -The window's start URL is automatically resolved from configuration in this priority order: - -1. `ASPNETCORE_URLS` environment variable -2. `urls` configuration key (e.g. in `appsettings.json`) -3. Manual override via `builder.Window.SetStartPageUrl(...)` +var builder = InfiniFrameApplication.CreateBuilder(args) + .WithWindow("web", window => window + .SetTitle("Web App") + .SetSize(1280, 720)); + +builder.UseWebServer("web", web => { + web.WebHost.UseUrls("http://127.0.0.1:5055"); + web.ConfigureWebApplication(application => { + application.UseRouting(); + application.MapGet("/health", () => Results.Ok()); + }); +}); -```json -{ - "urls": "http://localhost:5200" -} +builder.Build().Run(); ``` -If multiple URLs are configured (e.g. `"http://localhost:5200;https://localhost:7200"`), the first one is used as the window's start URL. +The server's bound address is resolved after startup. Wildcard bindings are converted to a loopback URL before navigation and origin trust are configured. -## Accessing the Window from ASP.NET Core +## Dependency Injection -`IInfiniFrameWindow` and `IInfiniFrameWindowBuilder` are registered in the web app's DI container: +Application services are copied into the ASP.NET Core service collection. Window APIs can therefore be injected into handlers: ```csharp -app.WebApp.MapGet("/close", (IInfiniFrameWindow window) => { +application.MapGet("/close", (IInfiniFrameWindow window) => { window.Close(); return Results.Ok(); }); ``` -```csharp -public class MyController(IInfiniFrameWindow window) : ControllerBase { - [HttpGet("minimize")] - public IActionResult Minimize() { - window.Invoke(() => { /* minimize logic */ }); - return Ok(); - } -} -``` - -> **Note:** Window operations that affect the native UI must be marshalled to the window thread using `window.Invoke(...)`. - -## Graceful Shutdown - -### UseAutoServerClose - -Automatically stops the web server when the window is closed or a close is requested: - -```csharp -var app = builder.Build().UseAutoServerClose(); -``` - -Internally this registers handlers on both `WindowClosing` and `WindowClosingRequested` that call `WebApp.StopAsync()` in a background task, so the UI thread is never blocked. - -### Manual shutdown - -```csharp -app.Stop(); // Stops the web server and closes the window -``` - -```csharp -await app.WebApp.StopAsync(); // Stop server only -app.Window.Close(); // Then close window -``` - -## Example: Blazor Server +## Blazor Server ```csharp +using InfiniFrame.Application; using InfiniFrame.WebServer; -var builder = InfiniFrameWebApplication.CreateBuilder(args); - -builder.Window - .SetTitle("Blazor Server App") - .SetSize(1280, 720) - .Center(); +var builder = InfiniFrameApplication.CreateBuilder(args) + .WithWindow(window => window.SetTitle("Blazor Server")); -builder.WebApp.Services.AddRazorComponents() +builder.Services.AddRazorComponents() .AddInteractiveServerComponents(); -var app = builder.Build().UseAutoServerClose(); +builder.UseWebServer(web => web.ConfigureWebApplication(application => + application.MapRazorComponents().AddInteractiveServerRenderMode())); -app.WebApp.UseStaticFiles(); -app.WebApp.UseAntiforgery(); -app.WebApp.MapRazorComponents() - .AddInteractiveServerRenderMode(); - -app.Run(); +builder.Build().Run(); ``` -## Static Web Assets - -`UseStaticWebAssets()` is called automatically during builder initialization, so static files from Razor class libraries are served correctly without additional configuration. - -`UseDefaultFiles()` is also applied during `Build()`, which causes requests to `/` to serve `wwwroot/index.html` if it exists. - -## Thread Model - -| Thread | Runs | -|-------------------|---------------------------| -| Main thread | Native window (UI thread) | -| Background thread | ASP.NET Core / Kestrel | - -All window API calls from ASP.NET Core handlers must use `window.Invoke(...)` to marshal to the window thread. -Web server calls from window event handlers can be made directly since ASP.NET Core is thread-safe. - -> **Windows:** The main thread must be STA. Add `[STAThread]` to your `Main` method and use an explicit `static void Main()`. Top-level statements and `async Task Main` do not support STA correctly. - -## Examples - -- `InfiniFrameExample.WebApp.Blazor` (`examples/InfiniFrameExample.WebApp.Blazor`) - Blazor Server with InteractiveServerComponents, HttpClient factory, and InfiniFrameJs -- `InfiniFrameExample.WebApp.React` (`examples/InfiniFrameExample.WebApp.React`) - React frontend with custom scheme handler and two-way messaging -- `InfiniFrameExample.WebApp.Vue` (`examples/InfiniFrameExample.WebApp.Vue`) - Vue.js frontend with all built-in JS message handlers - -## See Also - -- [Invoke Feature](invoke-feature.md) Cross-thread dispatch to the window's native thread -- [Window Features Architecture](window-features-architecture.md) How the feature system works -- [Core Window Guide](core-window.md) Builder API and feature overview +For static assets, configure the appropriate ASP.NET Core static-file or static-asset middleware in `ConfigureWebApplication`. diff --git a/docs/docs/guides/window-features-architecture.md b/docs/docs/guides/window-features-architecture.md index 21beb5e81..9d7535a3d 100644 --- a/docs/docs/guides/window-features-architecture.md +++ b/docs/docs/guides/window-features-architecture.md @@ -37,7 +37,7 @@ 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. +Builder features apply their settings to `InfiniFrameNativeWindowParameters`, which the native layer reads during window creation. After `Build()`, builder-only settings cannot be changed. ### Runtime Feature (`IInfiniFrameWindowFeature`) @@ -134,7 +134,7 @@ builder.Features.InstanceArbitration // IInstanceArbitrationInfiniFrameWindowBui ## How Features Are Wired -1. **Builder phase**: Each builder feature implements `IInfiniFrameWindowBuilderFeature` with an `ApplyToNativeParameters(ref InfiniFrameNativeParameters)` method. The builder collects all settings into a native parameters struct. +1. **Builder phase**: Each builder feature implements `IInfiniFrameWindowBuilderFeature` with an `ApplyToNativeParameters(ref InfiniFrameNativeWindowParameters)` method. The builder collects all settings into a native parameters struct. 2. **Build**: `InfiniFrameWindowBuilder.Build()` creates the window, then `InfiniFrameWindowFeaturesFactory` creates all runtime feature instances from the DI container, passing the window and the original builder. diff --git a/docs/docs/migration/photino-breaking-changes.md b/docs/docs/migration/photino-breaking-changes.md index 5d3bad0ae..66fd25cc4 100644 --- a/docs/docs/migration/photino-breaking-changes.md +++ b/docs/docs/migration/photino-breaking-changes.md @@ -32,7 +32,7 @@ For detailed documentation on the new feature-based API, see: | C# namespace | `Photino.NET` | `InfiniFrame` | | Native DLL | `Photino.Native` | `InfiniFrame.Native` (internal) | | C++ class | `Photino` | `InfiniFrameWindow` | -| C++ init params | `PhotinoInitParams` | `InfiniFrameInitParams` | +| C++ init params | `PhotinoInitParams` | `InfiniFrameWindowInitParams` | | Exported function prefix | `Photino_` | `InfiniFrameNative_` | | Default window title | `"Photino"` | `"InfiniFrame"` | | Default user agent | `"Photino WebView"` | `"InfiniFrame WebView"` | @@ -250,19 +250,22 @@ For BlazorWebView, InfiniFrame serves content from `app://localhost/` and valida The preferred migration pattern is to keep web security on and explicitly trust only the origins you need: ```csharp -var app = InfiniFrameBlazorAppBuilder.CreateDefault(windowBuilder: wb => { - wb.AddTrustedOrigin("https://xyz"); - wb.AddTrustedOrigin("https://cdn.jsdelivr.net"); - wb.AddTrustedOrigin("https://unpkg.com"); -}); +var builder = InfiniFrameApplication.CreateBuilder(args) + .WithWindow(window => window + .AddTrustedOrigin("https://xyz") + .AddTrustedOrigin("https://cdn.jsdelivr.net") + .AddTrustedOrigin("https://unpkg.com")); +builder.UseBlazorWebView(configuration => configuration.RootComponents.Add("app")); +var app = builder.Build(); ``` If you need broad compatibility during migration, you can opt in to trusting all origins: ```csharp -var app = InfiniFrameBlazorAppBuilder.CreateDefault(windowBuilder: wb => { - wb.SetTrustAllOrigins(true); -}); +var builder = InfiniFrameApplication.CreateBuilder(args) + .WithWindow(window => window.SetTrustAllOrigins(true)); +builder.UseBlazorWebView(configuration => configuration.RootComponents.Add("app")); +var app = builder.Build(); ``` `SetTrustAllOrigins(true)` is intentionally high-risk and should be treated as a temporary dev-time switch, not a production default. diff --git a/examples/InfiniFrameExample.BlazorWebView.MudBlazor/Components/App.razor b/examples/InfiniFrameExample.BlazorWebView.MudBlazor/Components/App.razor index dab47188f..e16e6152e 100644 --- a/examples/InfiniFrameExample.BlazorWebView.MudBlazor/Components/App.razor +++ b/examples/InfiniFrameExample.BlazorWebView.MudBlazor/Components/App.razor @@ -1,3 +1,4 @@ +@using global::MudBlazor diff --git a/examples/InfiniFrameExample.BlazorWebView.MudBlazor/Components/Pages/Index.razor b/examples/InfiniFrameExample.BlazorWebView.MudBlazor/Components/Pages/Index.razor index 053720acd..666add529 100644 --- a/examples/InfiniFrameExample.BlazorWebView.MudBlazor/Components/Pages/Index.razor +++ b/examples/InfiniFrameExample.BlazorWebView.MudBlazor/Components/Pages/Index.razor @@ -98,7 +98,7 @@ @if (_eventLog.Count == 0) { No events yet. Click a button above. } - @foreach (var entry in _eventLog) { + @foreach (string entry in _eventLog) { @entry diff --git a/examples/InfiniFrameExample.BlazorWebView.MudBlazor/Program.cs b/examples/InfiniFrameExample.BlazorWebView.MudBlazor/Program.cs index 5b4217353..8106aa4c7 100644 --- a/examples/InfiniFrameExample.BlazorWebView.MudBlazor/Program.cs +++ b/examples/InfiniFrameExample.BlazorWebView.MudBlazor/Program.cs @@ -1,14 +1,15 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- +using InfiniFrameExample.BlazorWebView.MudBlazor.Components; +using Serilog; +using System.Drawing; using InfiniFrame; +using InfiniFrame.Application; using InfiniFrame.BlazorWebView; -using InfiniFrameExample.BlazorWebView.MudBlazor.Components; -using MudBlazor.Services; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using Serilog; -using System.Drawing; +using MudBlazor.Services; namespace InfiniFrameExample.BlazorWebView.MudBlazor; // --------------------------------------------------------------------------------------------------------------------- @@ -25,9 +26,12 @@ private static void Main(string[] args) { try { Log.Information("Starting InfiniFrame BlazorWebView MudBlazor example..."); - var appBuilder = InfiniFrameBlazorAppBuilder.CreateDefault(args); - - appBuilder.Services + InfiniFrameApplicationBuilder builder = InfiniFrameApplication.CreateBuilder(args); + builder.WithWindow(window => window + .SetIconFile("wwwroot/favicon.ico") + .SetLocation(new Point(100, 100)) + .SetSize(new Size(800, 600))); + builder.Services .AddLogging(config => { config.ClearProviders(); config.AddSerilog(); @@ -38,17 +42,11 @@ private static void Main(string[] args) { }) .AddMudServices(); - appBuilder.RootComponents.Add("app"); - - appBuilder.WithInfiniFrameWindowBuilder(builder => { - builder - .SetIconFile("wwwroot/favicon.ico") - .SetLocation(new Point(100, 100)) - .SetSize(new Size(800, 600)); - }); - - Log.Information("Building InfiniFrame application..."); - InfiniFrameBlazorApp app = appBuilder.Build(); + InfiniFrameApplication app = builder + .UseBlazorWebView(configuration => { + configuration.RootComponents.Add("app"); + }) + .Build(); Log.Information("Running application..."); app.Run(); diff --git a/examples/InfiniFrameExample.BlazorWebView/Program.cs b/examples/InfiniFrameExample.BlazorWebView/Program.cs index 6fb0fd100..b619ca4a7 100644 --- a/examples/InfiniFrameExample.BlazorWebView/Program.cs +++ b/examples/InfiniFrameExample.BlazorWebView/Program.cs @@ -1,13 +1,14 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- +using System.Drawing; using InfiniFrame; +using InfiniFrame.Application; using InfiniFrame.BlazorWebView; using InfiniFrameExample.BlazorWebView.Components; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Serilog; -using System.Drawing; namespace InfiniFrameExample.BlazorWebView; // --------------------------------------------------------------------------------------------------------------------- @@ -16,41 +17,32 @@ namespace InfiniFrameExample.BlazorWebView; public static class Program { [STAThread] private static void Main(string[] args) { - var appBuilder = InfiniFrameBlazorAppBuilder.CreateDefault(args); + InfiniFrameApplicationBuilder builder = InfiniFrameApplication.CreateBuilder(args); - appBuilder.Services.AddLogging(config => { - config.ClearProviders(); - config.AddSerilog(); + builder.Services.AddLogging(config => { + config.ClearProviders(); + config.AddSerilog(); }); - appBuilder.Services.AddSerilog(config => { - config.WriteTo.Async(static c => c.Console()) - .MinimumLevel.Debug(); + builder.Services.AddSerilog(config => { + config.WriteTo.Async(static c => c.Console()) + .MinimumLevel.Debug(); }); - // register the root component and selector - appBuilder.RootComponents.Add("app"); - - appBuilder.WithInfiniFrameWindowBuilder(builder => { - builder - // .SetTransparent(true) - // .SetChromeless(true) - // .SetResizable(true) - .SetIconFile("wwwroot/favicon.ico") - .SetWindowsAppUserModelId("InfiniLore.InfiniFrameExample.BlazorWebView") - // .Center() - // .SetUseOsDefaultSize(true) - // .SetUseOsDefaultLocation(true); - // .SetTitle("InfiniLore InfiniFrame.Blazor Sample") - .SetLocation(new Point(100, 100)) - .SetSize(new Size(800, 600)) - // .SetMaxSize(new Size(800, 600)) - // .SetMinSize(new Size(600, 400)) - ; + builder.WithWindow(window => window + .SetIconFile("wwwroot/favicon.ico") + .SetLocation(new Point(100, 100)) + .SetSize(new Size(800, 600))); + + + builder.UseBlazorWebView(configuration => { + // register the root component and selector + configuration.RootComponents.Add("app"); + }); - InfiniFrameBlazorApp app = appBuilder.Build(); + InfiniFrameApplication app = builder.Build(); app.Run(); } -} \ No newline at end of file +} diff --git a/examples/InfiniFrameExample.BlazorWebView/README.md b/examples/InfiniFrameExample.BlazorWebView/README.md index f6e4249ca..2236ee026 100644 --- a/examples/InfiniFrameExample.BlazorWebView/README.md +++ b/examples/InfiniFrameExample.BlazorWebView/README.md @@ -5,7 +5,7 @@ Demonstrates the minimal setup for hosting a Blazor application inside a native ## What it shows -- `InfiniFrameBlazorAppBuilder.CreateDefault()` entry point +- `InfiniFrameApplication.CreateBuilder()` entry point - Registering a root Blazor component (``) mapped to the `#app` selector - Configuring the window: size, position, and icon file - Integrating Serilog for structured logging via `Microsoft.Extensions.Logging` @@ -20,15 +20,17 @@ dotnet run --project examples/InfiniFrameExample.BlazorWebView ## Key code ```csharp -var appBuilder = InfiniFrameBlazorAppBuilder.CreateDefault(args); +var appBuilder = InfiniFrameApplication.CreateBuilder(args); -appBuilder.RootComponents.Add("app"); +appBuilder.UseBlazorWebView(config => { + config.RootComponents.Add("app"); -appBuilder.WithInfiniFrameWindowBuilder(builder => builder + config.ConfigureWindow(builder => builder .SetIconFile("favicon.ico") .SetLocation(new Point(100, 100)) .SetSize(new Size(800, 600)) -); + ); +}); appBuilder.Build().Run(); ``` diff --git a/examples/InfiniFrameExample.NativeMenu/Program.cs b/examples/InfiniFrameExample.NativeMenu/Program.cs index e7812b714..3185dfc4b 100644 --- a/examples/InfiniFrameExample.NativeMenu/Program.cs +++ b/examples/InfiniFrameExample.NativeMenu/Program.cs @@ -4,6 +4,7 @@ using InfiniFrame; using System.Drawing; using System.Text.Json; +using InfiniFrame.Application; namespace InfiniFrameExample.NativeMenu; // --------------------------------------------------------------------------------------------------------------------- @@ -71,18 +72,19 @@ public static void Main(string[] args) { ] ); - IInfiniFrameWindow window = InfiniFrameWindowBuilder.Create() - .SetTitle("InfiniFrame Native Menu Example") - .SetSize(new Size(960, 640)) - .CenteredOnMainMonitor() - .SetMenuBar(menuBar) - .UseEmbeddedWwwrootAssets( - scheme: "app", - includePhysicalFallback: true, - physicalWwwrootPath: Path.Join(AppContext.BaseDirectory, "wwwroot"), - setStartUrl: true - ) - .RegisterWebMessageReceivedHandler((win, message) => { + InfiniFrameApplication.CreateBuilder(args) + .WithWindow(builder => builder + .SetTitle("InfiniFrame Native Menu Example") + .SetSize(new Size(960, 640)) + .CenteredOnMainMonitor() + .SetMenuBar(menuBar) + .UseEmbeddedWwwrootAssets( + scheme: "app", + includePhysicalFallback: true, + physicalWwwrootPath: Path.Join(AppContext.BaseDirectory, "wwwroot"), + setStartUrl: true + ) + .RegisterWebMessageReceivedHandler((win, message) => { string? action = ExtractAction(message); if (action == null) return; @@ -109,10 +111,9 @@ public static void Main(string[] args) { win.SendWebMessage($"status:Action: {action}"); break; } - }) - .Build(); - - window.WaitForClose(); + })) + .Build() + .Run(); } private static string? ExtractAction(string rawMessage) { diff --git a/examples/InfiniFrameExample.TrimAotSmoke/Program.cs b/examples/InfiniFrameExample.TrimAotSmoke/Program.cs index 9e413fde6..e3eb57632 100644 --- a/examples/InfiniFrameExample.TrimAotSmoke/Program.cs +++ b/examples/InfiniFrameExample.TrimAotSmoke/Program.cs @@ -2,6 +2,7 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniFrame.Application; namespace InfiniFrameExample.TrimAotSmoke; @@ -11,13 +12,13 @@ namespace InfiniFrameExample.TrimAotSmoke; public static class Program { [STAThread] public static void Main() { - IInfiniFrameWindow window = InfiniFrameWindowBuilder.Create() - .SetTitle("InfiniFrame Trim/AOT Smoke") - .SetSize(800, 600) - .CenteredOnMainMonitor() - .UseEmbeddedWwwrootAssets() - .Build(); - - window.WaitForClose(); + InfiniFrameApplication.CreateBuilder() + .WithWindow(builder => builder + .SetTitle("InfiniFrame Trim/AOT Smoke") + .SetSize(800, 600) + .CenteredOnMainMonitor() + .UseEmbeddedWwwrootAssets()) + .Build() + .Run(); } -} \ No newline at end of file +} diff --git a/examples/README.md b/examples/README.md index 9fe382eb2..fa78dee05 100644 --- a/examples/README.md +++ b/examples/README.md @@ -24,7 +24,7 @@ All examples require the native `InfiniFrame.Native` library to be built first ```bash # From the repo root build the native library for your platform -cmake -S src/InfiniFrame.Native -B artifacts/native/windows/x64/Debug -DCMAKE_BUILD_TYPE=Debug +cmake -S src/InfiniFrame.NativeBridge/Native -B artifacts/native/windows/x64/Debug -DCMAKE_BUILD_TYPE=Debug cmake --build artifacts/native/windows/x64/Debug # Then run an example diff --git a/examples/SingleFileExe/InfiniFrameExample.SingleFileExe.MudBlazor/Components/App.razor b/examples/SingleFileExe/InfiniFrameExample.SingleFileExe.MudBlazor/Components/App.razor index bc8e178c9..021aae981 100644 --- a/examples/SingleFileExe/InfiniFrameExample.SingleFileExe.MudBlazor/Components/App.razor +++ b/examples/SingleFileExe/InfiniFrameExample.SingleFileExe.MudBlazor/Components/App.razor @@ -1,4 +1,5 @@ - +@using global::MudBlazor + diff --git a/examples/SingleFileExe/InfiniFrameExample.SingleFileExe.MudBlazor/Program.cs b/examples/SingleFileExe/InfiniFrameExample.SingleFileExe.MudBlazor/Program.cs index 09b29ccad..898f5dae3 100644 --- a/examples/SingleFileExe/InfiniFrameExample.SingleFileExe.MudBlazor/Program.cs +++ b/examples/SingleFileExe/InfiniFrameExample.SingleFileExe.MudBlazor/Program.cs @@ -2,8 +2,10 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniFrame.Application; using InfiniFrame.BlazorWebView; using InfiniFrame.SingleFile; +using InfiniFrame.Window.Features.WebMessaging.Handlers; using InfiniFrameExample.SingleFileExe.MudBlazor.Components; using MudBlazor.Services; using Serilog; @@ -25,9 +27,11 @@ private static void Main(string[] args) { try { Log.Information("Starting InfiniFrame MudBlazor example..."); - var appBuilder = InfiniFrameBlazorAppBuilder.CreateDefault(args); - - appBuilder.Services + InfiniFrameApplicationBuilder builder = InfiniFrameApplication.CreateBuilder(args); + builder.WithWindow(window => window + .SetIconFile("wwwroot/favicon.ico") + .RegisterOpenExternalTargetWebMessageHandler()); + builder.Services .AddLogging(config => { config.ClearProviders(); config.AddSerilog(); @@ -37,17 +41,12 @@ private static void Main(string[] args) { .MinimumLevel.Debug(); }) .AddMudServices(); - - appBuilder.RootComponents.Add("app"); - - appBuilder.WindowBuilder - .SetIconFile("wwwroot/favicon.ico") - .RegisterOpenExternalTargetWebMessageHandler(); - - InfiniFrameSingleFile.AddSingleFileRequirements(appBuilder); - - Log.Information("Building InfiniFrame application..."); - InfiniFrameBlazorApp application = appBuilder.Build(); + InfiniFrameApplication application = builder + .UseBlazorWebView(configuration => { + configuration.RootComponents.Add("app"); + configuration.AddSingleFileRequirements(); + }) + .Build(); Log.Information("Running application..."); application.Run(); diff --git a/examples/SingleFileExe/InfiniFrameExample.SingleFileExe.React/Program.cs b/examples/SingleFileExe/InfiniFrameExample.SingleFileExe.React/Program.cs index e2817d90c..c37e87912 100644 --- a/examples/SingleFileExe/InfiniFrameExample.SingleFileExe.React/Program.cs +++ b/examples/SingleFileExe/InfiniFrameExample.SingleFileExe.React/Program.cs @@ -1,5 +1,6 @@ -using InfiniFrame; using System.Drawing; +using InfiniFrame; +using InfiniFrame.Application; using InfiniFrame.SingleFile; namespace InfiniFrameExample.SingleFileExe.React; @@ -9,14 +10,15 @@ public static class Program { public static void Main(string[] args) { InfiniFrameSingleFile.Initialize(); - IInfiniFrameWindowBuilder builder = InfiniFrameWindowBuilder.Create() - .SetTitle("InfiniFrame + React") - .SetSize(new Size(960, 640)) - .CenteredOnMainMonitor(); - - builder.AddSingleFileRequirements(); - - IInfiniFrameWindow window = builder.Build(); - window.WaitForClose(); + InfiniFrameApplication.CreateBuilder(args) + .WithWindow(builder => { + builder + .SetTitle("InfiniFrame + React") + .SetSize(new Size(960, 640)) + .CenteredOnMainMonitor(); + builder.AddSingleFileRequirements(); + }) + .Build() + .Run(); } } diff --git a/examples/SingleFileExe/InfiniFrameExample.SingleFileExe.Vue/Program.cs b/examples/SingleFileExe/InfiniFrameExample.SingleFileExe.Vue/Program.cs index 866c60e20..729e22acc 100644 --- a/examples/SingleFileExe/InfiniFrameExample.SingleFileExe.Vue/Program.cs +++ b/examples/SingleFileExe/InfiniFrameExample.SingleFileExe.Vue/Program.cs @@ -1,8 +1,9 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame; using System.Drawing; +using InfiniFrame; +using InfiniFrame.Application; using InfiniFrame.SingleFile; namespace InfiniFrameExample.SingleFileExe.Vue; @@ -14,14 +15,15 @@ public static class Program { public static void Main(string[] args) { InfiniFrameSingleFile.Initialize(); - IInfiniFrameWindowBuilder builder = InfiniFrameWindowBuilder.Create() - .SetTitle("InfiniFrame + Vue") - .SetSize(new Size(960, 640)) - .CenteredOnMainMonitor(); - - builder.AddSingleFileRequirements(); - - IInfiniFrameWindow window = builder.Build(); - window.WaitForClose(); + InfiniFrameApplication.CreateBuilder(args) + .WithWindow(builder => { + builder + .SetTitle("InfiniFrame + Vue") + .SetSize(new Size(960, 640)) + .CenteredOnMainMonitor(); + builder.AddSingleFileRequirements(); + }) + .Build() + .Run(); } } diff --git a/examples/SingleFileExe/InfiniFrameExample.SingleFileExe/Program.cs b/examples/SingleFileExe/InfiniFrameExample.SingleFileExe/Program.cs index 10387ee05..61a9fab20 100644 --- a/examples/SingleFileExe/InfiniFrameExample.SingleFileExe/Program.cs +++ b/examples/SingleFileExe/InfiniFrameExample.SingleFileExe/Program.cs @@ -1,8 +1,9 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame; using System.Drawing; +using InfiniFrame; +using InfiniFrame.Application; using InfiniFrame.SingleFile; namespace InfiniFrameExample.SingleFileExe; @@ -14,15 +15,15 @@ public static class Program { public static void Main(string[] args) { InfiniFrameSingleFile.Initialize(); - IInfiniFrameWindowBuilder builder = InfiniFrameWindowBuilder.Create() - .SetTitle("InfiniFrame Embedded wwwroot") - .SetSize(new Size(960, 640)) - .CenteredOnMainMonitor(); - - builder.AddSingleFileRequirements(); - - IInfiniFrameWindow window = builder.Build(); - - window.WaitForClose(); + InfiniFrameApplication.CreateBuilder(args) + .WithWindow(builder => { + builder + .SetTitle("InfiniFrame Embedded wwwroot") + .SetSize(new Size(960, 640)) + .CenteredOnMainMonitor(); + builder.AddSingleFileRequirements(); + }) + .Build() + .Run(); } } diff --git a/examples/WebApp/InfiniFrameExample.WebApp.Blazor/Program.cs b/examples/WebApp/InfiniFrameExample.WebApp.Blazor/Program.cs index 65969dbf3..f660c0de8 100644 --- a/examples/WebApp/InfiniFrameExample.WebApp.Blazor/Program.cs +++ b/examples/WebApp/InfiniFrameExample.WebApp.Blazor/Program.cs @@ -1,11 +1,13 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- +using System.Drawing; using InfiniFrame; +using InfiniFrame.Application; using InfiniFrame.WebServer; +using InfiniFrame.Window.Features.WebMessaging.Handlers; using InfiniFrameExample.WebApp.Blazor.Components; using Serilog; -using System.Drawing; namespace InfiniFrameExample.WebApp.Blazor; // --------------------------------------------------------------------------------------------------------------------- @@ -17,9 +19,16 @@ private static void Main(string[] args) { // ------------------------------------------------------------------------------------------------------------- // Builder // ------------------------------------------------------------------------------------------------------------- - InfiniFrameWebApplicationBuilder appBuilder = InfiniFrameWebApplication.CreateBuilder(args); + InfiniFrameApplicationBuilder builder = InfiniFrameApplication.CreateBuilder(args); - appBuilder.Services + builder.WithWindow(window => window + .SetIconFile("wwwroot/favicon.ico") + .SetLocation(new Point(100, 100)) + .SetSize(new Size(800, 600)) + .RegisterOpenExternalTargetWebMessageHandler() + ); + + builder.Services .AddLogging(config => { config.ClearProviders(); config.AddSerilog(); @@ -31,7 +40,7 @@ private static void Main(string[] args) { .AddRazorComponents() .AddInteractiveServerComponents(); - appBuilder.Services.AddHttpClient("ServerApi", (sp, client) => { + builder.Services.AddHttpClient("ServerApi", configureClient: (sp, client) => { var config = sp.GetRequiredService(); // Prefer ASPNETCORE_URLS, then "urls", then a fallback @@ -45,44 +54,24 @@ private static void Main(string[] args) { client.BaseAddress = new Uri(baseUrl); }); - appBuilder.Services.AddScoped(sp => sp.GetRequiredService().CreateClient("ServerApi")); - - appBuilder.Services.AddInfiniFrameJs(); - - appBuilder.WebApp.WebHost.UseStaticWebAssets(); + builder.Services.AddScoped(sp => sp.GetRequiredService().CreateClient("ServerApi")); - appBuilder.WindowBuilder - // .SetTransparent(true) - // .SetChromeless(true) - // .SetResizable(true) - .SetIconFile("wwwroot/favicon.ico") - // .Center() - // .SetUseOsDefaultSize(true) - // .SetUseOsDefaultLocation(true); - // .SetTitle("InfiniLore InfiniFrame.Blazor Sample") - .SetLocation(new Point(100, 100)) - .SetSize(new Size(800, 600)) - .RegisterOpenExternalTargetWebMessageHandler() - // .SetMaxSize(new Size(800, 600)) - // .SetMinSize(new Size(600, 400)) - ; + builder.Services.AddInfiniFrameJs(); - // ------------------------------------------------------------------------------------------------------------- - // App - // ------------------------------------------------------------------------------------------------------------- - InfiniFrameWebApplication application = appBuilder.Build(); - application.UseAutoServerClose(); - - WebApplication webApp = application.WebApp; + builder.UseWebServer(web => { - webApp.UseRouting(); + web.WebHost.UseStaticWebAssets(); - webApp.UseAntiforgery(); - webApp.MapStaticAssets(); - - webApp.MapRazorComponents() - .AddInteractiveServerRenderMode(); + web.ConfigureWebApplication(webApp => { + webApp.UseRouting(); + webApp.UseAntiforgery(); + webApp.MapStaticAssets(); + webApp.MapRazorComponents() + .AddInteractiveServerRenderMode(); + }); + }); + InfiniFrameApplication application = builder.Build(); application.Run(); } -} \ No newline at end of file +} diff --git a/examples/WebApp/InfiniFrameExample.WebApp.Blazor/README.md b/examples/WebApp/InfiniFrameExample.WebApp.Blazor/README.md index 848223dc4..c9793bdb0 100644 --- a/examples/WebApp/InfiniFrameExample.WebApp.Blazor/README.md +++ b/examples/WebApp/InfiniFrameExample.WebApp.Blazor/README.md @@ -4,12 +4,12 @@ Demonstrates hosting a full ASP.NET Core Blazor Server application inside a nati ## What it shows -- `InfiniFrameWebApplication.CreateBuilder()` entry point +- `InfiniFrameApplication.CreateBuilder()` entry point - Blazor Server with `AddRazorComponents()` + `AddInteractiveServerComponents()` - `HttpClient` factory configured to point at the local Kestrel server - `AddInfiniFrameJs()` service registration for Blazor component interop - `RegisterOpenExternalTargetWebMessageHandler()` links with `target="_blank"` open in the default browser -- `UseAutoServerClose()` server stops when the window is closed +- Application-owned lifecycle stops the server when the window is closed - Serilog with async console sink ## Run @@ -21,19 +21,19 @@ dotnet run --project examples/InfiniFrameExample.WebApp.Blazor ## Key code ```csharp -InfiniFrameWebApplicationBuilder builder = InfiniFrameWebApplication.CreateBuilder(args); +InfiniFrameApplicationBuilder builder = InfiniFrameApplication.CreateBuilder(args); -builder.WebApp.Services.AddRazorComponents() +builder.Services.AddRazorComponents() .AddInteractiveServerComponents(); -builder.WebApp.Services.AddInfiniFrameJs(); +builder.Services.AddInfiniFrameJs(); -builder.Window +builder.WithWindow(window => window .SetSize(new Size(800, 600)) - .RegisterOpenExternalTargetWebMessageHandler(); + .RegisterOpenExternalTargetWebMessageHandler()); -InfiniFrameWebApplication app = builder.Build(); -app.UseAutoServerClose(); -app.WebApp.MapRazorComponents().AddInteractiveServerRenderMode(); +builder.UseWebServer(web => web.ConfigureWebApplication(application => + application.MapRazorComponents().AddInteractiveServerRenderMode())); +InfiniFrameApplication app = builder.Build(); app.Run(); ``` diff --git a/examples/WebApp/InfiniFrameExample.WebApp.React/Program.cs b/examples/WebApp/InfiniFrameExample.WebApp.React/Program.cs index b32b42dcc..3e88cbcd3 100644 --- a/examples/WebApp/InfiniFrameExample.WebApp.React/Program.cs +++ b/examples/WebApp/InfiniFrameExample.WebApp.React/Program.cs @@ -2,8 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.WebServer; using System.Drawing; +using InfiniFrame.Application; +using InfiniFrame.WebServer; namespace InfiniFrameExample.WebApp.React; // --------------------------------------------------------------------------------------------------------------------- @@ -17,11 +18,10 @@ private sealed class WebMessageCounter { [STAThread] public static void Main(string[] args) { - InfiniFrameWebApplicationBuilder appBuilder = InfiniFrameWebApplication.CreateBuilder(args); - // WebApplicationBuilder appBuilder = builder.WebApp; - appBuilder.WebApp.Services.AddSingleton(); - - appBuilder.WindowBuilder + InfiniFrameApplicationBuilder rootBuilder = InfiniFrameApplication.CreateBuilder(args); + rootBuilder.Services.AddSingleton(); + InfiniFrameApplication application = rootBuilder + .WithWindow(window => window .UseOsDefaultSize(false) .SetResizable() .CenteredOnMainMonitor() @@ -39,19 +39,18 @@ public static void Main(string[] args) { ]) , "text/javascript") ) - .RegisterWebMessageReceivedHandler((IInfiniFrameWindow window, string message, WebMessageCounter counter) => { + .RegisterWebMessageReceivedHandler((IInfiniFrameWindow infiniFrameWindow, string message, WebMessageCounter counter) => { int count = counter.Increment(); string response = $"[{count}] Received message: \"{message}\""; - window.SendWebMessage(response); - }); - - InfiniFrameWebApplication application = appBuilder.Build(); - - application.UseAutoServerClose(); - - application.WebApp.UseStaticFiles(); - application.WebApp.MapStaticAssets(); + infiniFrameWindow.SendWebMessage(response); + })) + .UseWebServer(builder => { + builder.ConfigureWebApplication(webApp => { + webApp.MapStaticAssets(); + }); + }) + .Build(); application.Run(); } -} \ No newline at end of file +} diff --git a/examples/WebApp/InfiniFrameExample.WebApp.React/README.md b/examples/WebApp/InfiniFrameExample.WebApp.React/README.md index 590377a09..9b770d96e 100644 --- a/examples/WebApp/InfiniFrameExample.WebApp.React/README.md +++ b/examples/WebApp/InfiniFrameExample.WebApp.React/README.md @@ -7,7 +7,7 @@ Demonstrates a React frontend served by ASP.NET Core inside an InfiniFrame windo - `RegisterCustomSchemeHandler("app", ...)` intercepts `app://` requests and returns dynamically generated JavaScript - `RegisterWebMessageReceivedHandler(...)` receives messages from JavaScript, increments a counter, and echoes a response back via `SendWebMessage` - A singleton `WebMessageCounter` service accessed inside the message handler via DI -- `UseAutoServerClose()` server stops when the window is closed +- Application-owned lifecycle stops the server when the window is closed ## Run diff --git a/examples/WebApp/InfiniFrameExample.WebApp.Vue/Program.cs b/examples/WebApp/InfiniFrameExample.WebApp.Vue/Program.cs index 50a6541e9..93e073071 100644 --- a/examples/WebApp/InfiniFrameExample.WebApp.Vue/Program.cs +++ b/examples/WebApp/InfiniFrameExample.WebApp.Vue/Program.cs @@ -1,9 +1,11 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- +using System.Drawing; using InfiniFrame; +using InfiniFrame.Application; using InfiniFrame.WebServer; -using System.Drawing; +using InfiniFrame.Window.Features.WebMessaging.Handlers; namespace InfiniFrameExample.WebApp.Vue; // --------------------------------------------------------------------------------------------------------------------- @@ -12,37 +14,35 @@ namespace InfiniFrameExample.WebApp.Vue; public static class Program { [STAThread] public static void Main(string[] args) { - InfiniFrameWebApplicationBuilder appBuilder = InfiniFrameWebApplication.CreateBuilder(args); - // WebApplicationBuilder appBuilder = builder.WebApp; - - if (OperatingSystem.IsWindows() || OperatingSystem.IsLinux()) appBuilder.WindowBuilder.Debugging.SetRemoteDebuggingPort(9222); - - appBuilder.WindowBuilder - .CenteredOnMainMonitor() - // .SetTransparent(true) - // .SetUseOsDefaultSize(false) - .SetTitle("InfiniLore InfiniFrame.NET VUE Sample") - .SetSize(new Size(800, 600)) - .SetLocation(1000, 0) - .RegisterFullScreenWebMessageHandler() - .RegisterOpenExternalTargetWebMessageHandler() - .RegisterTitleChangedWebMessageHandler() - .RegisterWindowManagementWebMessageHandler() - .RegisterWebMessageReceivedHandler((_, message) => { - // ReSharper disable twice UnusedVariable - string response = $"Received message: \"{message}\""; - - // ... do something with the message + InfiniFrameApplication application = InfiniFrameApplication.CreateBuilder(args) + .WithWindow(window => { + if (OperatingSystem.IsWindows() || OperatingSystem.IsLinux()) window.Debugging.SetRemoteDebuggingPort(9222); + window + .CenteredOnMainMonitor() + // .SetTransparent(true) + // .SetUseOsDefaultSize(false) + .SetTitle("InfiniLore InfiniFrame.NET VUE Sample") + .SetSize(new Size(800, 600)) + .SetLocation(1000, 0) + .RegisterFullScreenWebMessageHandler() + .RegisterOpenExternalTargetWebMessageHandler() + .RegisterTitleChangedWebMessageHandler() + .RegisterWindowManagementWebMessageHandler() + .RegisterWebMessageReceivedHandler((_, message) => { + // ReSharper disable twice UnusedVariable + string response = $"Received message: \"{message}\""; + + // ... do something with the message + }) + ; }) - ; - - InfiniFrameWebApplication application = appBuilder.Build(); - - application.UseAutoServerClose(); - - application.WebApp.UseStaticFiles(); - application.WebApp.MapStaticAssets(); + .UseWebServer(builder => { + builder.ConfigureWebApplication(webApp => { + webApp.MapStaticAssets(); + }); + }) + .Build(); application.Run(); } -} \ No newline at end of file +} diff --git a/examples/WebApp/InfiniFrameExample.WebApp/Program.cs b/examples/WebApp/InfiniFrameExample.WebApp/Program.cs index 8de617cc8..4de4afb2c 100644 --- a/examples/WebApp/InfiniFrameExample.WebApp/Program.cs +++ b/examples/WebApp/InfiniFrameExample.WebApp/Program.cs @@ -2,6 +2,7 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniFrame.Application; using InfiniFrame.WebServer; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; @@ -14,23 +15,20 @@ namespace InfiniFrameExample.WebApp; public static class Program { [STAThread] public static void Main(string[] args) { - InfiniFrameWebApplicationBuilder builder = - InfiniFrameWebApplication.CreateBuilder(args); - - builder.WebApp.WebHost.UseUrls("http://127.0.0.1:5055"); - builder.WindowBuilder - .SetStartPageUrl("http://127.0.0.1:5055") - .SetTitle("InfiniFrame WebServer Repro") - .SetIconFile("wwwroot/favicon.ico"); - - InfiniFrameWebApplication app = builder.Build(); - app.UseAutoServerClose(); - - app.WebApp.MapGet("/", handler: () => Results.Content( - "InfiniFrame loaded", - "text/html" - )); + InfiniFrameApplication app = InfiniFrameApplication.CreateBuilder(args) + .WithWindow("web", configure: window => window + .SetStartPageUrl("http://127.0.0.1:5055") + .SetTitle("InfiniFrame WebServer Repro") + .SetIconFile("wwwroot/favicon.ico")) + .UseWebServer("web", configure: builder => { + builder.WebHost.UseUrls("http://127.0.0.1:5055"); + builder.ConfigureWebApplication(webApp => webApp.MapGet("/", handler: () => Results.Content( + "InfiniFrame loaded", + "text/html" + ))); + }) + .Build(); app.Run(); } -} \ No newline at end of file +} diff --git a/examples/WebApp/InfiniFrameExample.WebApp/README.md b/examples/WebApp/InfiniFrameExample.WebApp/README.md index 6c1c51fa6..67eb03dfa 100644 --- a/examples/WebApp/InfiniFrameExample.WebApp/README.md +++ b/examples/WebApp/InfiniFrameExample.WebApp/README.md @@ -4,9 +4,9 @@ Demonstrates InfiniFrame's built-in ASP.NET Core web server integration. A local ## What It Shows -- `InfiniFrameWebApplicationBuilder` and `InfiniFrameWebApplication` API +- `InfiniFrameApplicationBuilder` and `InfiniFrameApplication` API - ASP.NET Core minimal APIs (`MapGet`) -- `UseAutoServerClose()` for graceful shutdown +- Application-owned lifecycle for graceful shutdown - Window-to-server DI integration ## Run diff --git a/scripts/clean.ps1 b/scripts/clean.ps1 index a9d2913bf..d6d4fe45a 100644 --- a/scripts/clean.ps1 +++ b/scripts/clean.ps1 @@ -24,10 +24,6 @@ $ExtraPaths = @( "../src/InfiniFrame.NativeBridge/build", "../src/InfiniFrame.NativeBridge/artifacts", "../src/InfiniFrame.Js/node_modules", - "../src/InfiniFrame.NativeBridge/Native/cmake-build-debug-linux", - "../src/InfiniFrame.NativeBridge/Native/cmake-build-debug-windows", - "../src/InfiniFrame.NativeBridge/Native/cmake-build-release-linux", - "../src/InfiniFrame.NativeBridge/Native/cmake-build-release-windows", "../src/InfiniFrame.NativeBridge/Native/packages" ) @@ -40,4 +36,4 @@ foreach ($RelativePath in $ExtraPaths) { } } -Write-Host "Done cleaning bin/obj folders and extra build artifacts." \ No newline at end of file +Write-Host "Done cleaning bin/obj folders and extra build artifacts." diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 90f2fe50f..0adc5409d 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -40,16 +40,22 @@ + + + + - + + + @@ -77,4 +83,4 @@ -
\ No newline at end of file + diff --git a/src/InfiniFrame.Application/ApplicationConfiguration.cs b/src/InfiniFrame.Application/ApplicationConfiguration.cs new file mode 100644 index 000000000..058f2a515 --- /dev/null +++ b/src/InfiniFrame.Application/ApplicationConfiguration.cs @@ -0,0 +1,22 @@ +// --------------------------------------------------------------------------------------------------------------------- +// Imports +// --------------------------------------------------------------------------------------------------------------------- +using InfiniFrame.NativeBridge.Parameters.Application; + +namespace InfiniFrame.Application; +// --------------------------------------------------------------------------------------------------------------------- +// Code +// --------------------------------------------------------------------------------------------------------------------- +internal sealed record ApplicationConfiguration( + string? WebView2RuntimePath = null, + string? NotificationRegistrationId = null, + string? AppUserModelId = null, + string? DefaultNotificationIcon = null +) { + internal InfiniFrameNativeApplicationParameters ToNativeParameters() => new() { + WebView2RuntimePath = WebView2RuntimePath, + NotificationRegistrationId = NotificationRegistrationId, + AppUserModelId = AppUserModelId, + DefaultNotificationIcon = DefaultNotificationIcon + }; +} diff --git a/src/InfiniFrame.Application/InfiniFrame.Application.csproj b/src/InfiniFrame.Application/InfiniFrame.Application.csproj new file mode 100644 index 000000000..0203b192b --- /dev/null +++ b/src/InfiniFrame.Application/InfiniFrame.Application.csproj @@ -0,0 +1,21 @@ + + + InfiniLore.InfiniFrame.Application + InfiniFrame application lifecycle and window orchestration. + + + + + + + + + + + + + + + + + diff --git a/src/InfiniFrame.Application/InfiniFrameApplication.cs b/src/InfiniFrame.Application/InfiniFrameApplication.cs new file mode 100644 index 000000000..161043667 --- /dev/null +++ b/src/InfiniFrame.Application/InfiniFrameApplication.cs @@ -0,0 +1,536 @@ +// --------------------------------------------------------------------------------------------------------------------- +// Imports +// --------------------------------------------------------------------------------------------------------------------- +using InfiniFrame.NativeBridge; +using InfiniFrame.NativeBridge.Handles; +using FluentValidation; +using InfiniFrame.NativeBridge.Parameters.Application; +using InfiniFrame.Utilities; +using InfiniFrame.Window.Builder; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; + +namespace InfiniFrame.Application; +// --------------------------------------------------------------------------------------------------------------------- +// Code +// --------------------------------------------------------------------------------------------------------------------- +/// +/// Application-level owner for lazily built InfiniFrame windows. +/// +public sealed class InfiniFrameApplication : IInfiniFrameApplication { + private readonly ILogger logger; + private readonly NativeApplicationHandle _nativeHandle; + private readonly object _gate = new(); + private readonly List<( + string? Id, + Action? Configure, + InfiniFrameWindowBuilder? Builder, + IServiceProvider? Provider + )> _registrations = []; + private readonly List> _shutdownActions = []; + private readonly List> _startupActions = []; + private readonly Dictionary _windows = []; + private IServiceProvider? _serviceProvider; + private int _disposed; + private int _runState; + private bool _built; + private int _shutdownRequested; + + private InfiniFrameApplication(ILogger logger, ApplicationConfiguration configuration) { + this.logger = logger; + InfiniFrameNativeApplicationParameters parameters = configuration.ToNativeParameters(); + new InfiniFrameNativeApplicationParametersValidator().ValidateAndThrow(parameters); + + InfiniFrameNativeInteropStatus status = InfiniFrameNative.ApplicationConstructor(out IntPtr handle); + if (status != InfiniFrameNativeInteropStatus.Success) + throw new InfiniFrameNativeInteropException(InfiniFrameNative.GetLastErrorMessage() ?? "Could not create native application."); + + _nativeHandle = new NativeApplicationHandle(handle); + InfiniFrameNativeInteropStatus configureStatus = InfiniFrameNative.ApplicationConfigure( + _nativeHandle.DangerousGetHandle(), + in parameters + ); + if (configureStatus != InfiniFrameNativeInteropStatus.Success) { + _nativeHandle.Dispose(); + throw new InfiniFrameNativeInteropException( + InfiniFrameNative.GetLastErrorMessage() ?? "Could not configure native application."); + } + } + + /// Creates an application without requiring a dependency-injection container. + public static InfiniFrameApplication Initialize() + => new(NullLogger.Instance, new ApplicationConfiguration()); + + internal static InfiniFrameApplication Initialize(ApplicationConfiguration configuration) + => new(NullLogger.Instance, configuration); + + public static InfiniFrameApplicationBuilder CreateBuilder(string[]? args = null) + => new(args); + + /// Creates an application using the supplied logger. + public static InfiniFrameApplication Initialize(ILogger logger) { + ArgumentNullException.ThrowIfNull(logger); + return new InfiniFrameApplication(logger, new ApplicationConfiguration()); + } + + public Guid Id { get; } = Guid.NewGuid(); + public IntPtr ApplicationHandle => _nativeHandle.DangerousGetHandle(); + public bool IsShutdownRequested => Volatile.Read(ref _shutdownRequested) != 0; + public event Action? WindowCreated; + public event Action? WindowDestroyed; + + /// + public void RegisterWindow(Action configure) { + ArgumentNullException.ThrowIfNull(configure); + RegisterWindowCore(null, configure, null); + } + + /// + public void RegisterWindow(string id, Action configure) { + ArgumentException.ThrowIfNullOrWhiteSpace(id); + ArgumentNullException.ThrowIfNull(configure); + RegisterWindowCore(id, configure, null); + } + + /// Registers an unnamed window and returns this application for fluent configuration. + public InfiniFrameApplication WithWindow(Action configure) { + RegisterWindow(configure); + return this; + } + + /// Registers a named window and returns this application for fluent configuration. + public InfiniFrameApplication WithWindow(string id, Action configure) { + RegisterWindow(id, configure); + return this; + } + + /// + public IInfiniFrameWindow GetWindow(string id) { + ArgumentException.ThrowIfNullOrWhiteSpace(id); + lock (_gate) { + EnsureBuilt(); + return _windows.TryGetValue(id, out IInfiniFrameWindow? window) + ? window + : throw new KeyNotFoundException($"Window with id '{id}' was not found."); + } + } + + /// + public IInfiniFrameWindow? TryGetWindow(string id) { + ArgumentException.ThrowIfNullOrWhiteSpace(id); + lock (_gate) return _built && _windows.TryGetValue(id, out IInfiniFrameWindow? window) ? window : null; + } + + /// + public IReadOnlyList Windows { + get { + lock (_gate) return _windows.Values.ToArray(); + } + } + + /// + public void Run() { + BeginRun(); + try { + EnsureWindowsStaThread(); + RegisterNativeApplication(); + if (IsShutdownRequested) return; + StartRegisteredComponents(); + BuildAllWindows(); + RunNativeLoop(); + } + finally { + Dispose(); + } + } + + /// + public async Task RunAsync(CancellationToken ct = default) { + BeginRun(); + await using CancellationTokenRegistration registration = ct.Register(Shutdown); + Task? uiTask = null; + try { + ct.ThrowIfCancellationRequested(); + await StartRegisteredComponentsAsync().ConfigureAwait(false); + ct.ThrowIfCancellationRequested(); + var completion = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var uiThread = new Thread(() => { + try { + if (IsShutdownRequested) { + completion.TrySetResult(); + return; + } + + RegisterNativeApplication(); + if (IsShutdownRequested) { + completion.TrySetResult(); + return; + } + + BuildAllWindows(); + if (IsShutdownRequested) { + CloseAll(); + } + + RunNativeLoop(); + completion.TrySetResult(); + } + catch (Exception exception) when (ExceptionsUtility.IsNonFatalException(exception)) { + completion.TrySetException(exception); + } + }) { + IsBackground = true, + Name = "InfiniFrame Application UI Thread" + }; + + if (OperatingSystem.IsWindows()) + uiThread.SetApartmentState(ApartmentState.STA); + uiThread.Start(); + uiTask = completion.Task; + await uiTask.ConfigureAwait(false); + } + finally { + try { + if (uiTask is not null) + await uiTask.ConfigureAwait(false); + } + finally { + await DisposeAsync().ConfigureAwait(false); + } + } + } + + /// + public void Shutdown() { + if (Volatile.Read(ref _disposed) != 0) return; + if (Interlocked.Exchange(ref _shutdownRequested, 1) != 0) return; + InfiniFrameNative.ApplicationShutdown(_nativeHandle.DangerousGetHandle()); + CloseAll(); + } + + /// + public void CloseAll() { + foreach (IInfiniFrameWindow window in Windows.ToArray()) { + try { + window.Close(); + } + catch (Exception exception) when (exception is ObjectDisposedException or InvalidOperationException) { + logger.LogDebug(exception, "Window was already unavailable during application shutdown."); + } + } + } + + private void RegisterNativeApplication() { + InfiniFrameNativeInteropStatus status = InfiniFrameNative.ApplicationRegister(_nativeHandle.DangerousGetHandle()); + if (status != InfiniFrameNativeInteropStatus.Success) + throw new InfiniFrameNativeInteropException( + InfiniFrameNative.GetLastErrorMessage() ?? "Could not register native application."); + } + + /// + public void Dispose() { + if (Interlocked.Exchange(ref _disposed, 1) != 0) return; + + IInfiniFrameWindow[] windows; + lock (_gate) { + windows = _windows.Values.ToArray(); + } + + Exception? windowDisposalFailure = null; + foreach (IInfiniFrameWindow window in windows) { + try { + (window as IDisposable)?.Dispose(); + if (window.LifecycleState != InfiniFrameWindowLifecycleState.Disposed) + throw new InvalidOperationException("A window did not complete native disposal."); + RemoveTrackedWindow(window); + } + catch (Exception ex) when (ex is not OutOfMemoryException and not StackOverflowException) { + logger.LogWarning(ex, "Failed to dispose an application window."); + windowDisposalFailure ??= ex; + } + } + if (windowDisposalFailure is not null) { + Volatile.Write(ref _disposed, 0); + throw new InvalidOperationException("The application could not dispose all windows.", windowDisposalFailure); + } + lock (_gate) { + _windows.Clear(); + _registrations.Clear(); + } + StopRegisteredComponents(); + _nativeHandle.Dispose(); + if (_serviceProvider is IAsyncDisposable asyncServiceProvider) + asyncServiceProvider.DisposeAsync().AsTask().GetAwaiter().GetResult(); + else (_serviceProvider as IDisposable)?.Dispose(); + _serviceProvider = null; + } + + /// + public async ValueTask DisposeAsync() { + if (Interlocked.Exchange(ref _disposed, 1) != 0) return; + + IInfiniFrameWindow[] windows; + lock (_gate) { + windows = _windows.Values.ToArray(); + } + + Exception? windowDisposalFailure = null; + foreach (IInfiniFrameWindow window in windows) { + try { + if (window is IAsyncDisposable asyncDisposable) + await asyncDisposable.DisposeAsync().ConfigureAwait(false); + else (window as IDisposable)?.Dispose(); + if (window.LifecycleState != InfiniFrameWindowLifecycleState.Disposed) + throw new InvalidOperationException("A window did not complete native disposal."); + RemoveTrackedWindow(window); + } + catch (Exception ex) when (ex is not OutOfMemoryException and not StackOverflowException) { + logger.LogWarning(ex, "Failed to asynchronously dispose an application window."); + windowDisposalFailure ??= ex; + } + } + if (windowDisposalFailure is not null) { + Volatile.Write(ref _disposed, 0); + throw new InvalidOperationException("The application could not dispose all windows.", windowDisposalFailure); + } + lock (_gate) { + _windows.Clear(); + _registrations.Clear(); + } + await StopRegisteredComponentsAsync().ConfigureAwait(false); + _nativeHandle.Dispose(); + if (_serviceProvider is IAsyncDisposable asyncServiceProvider) + await asyncServiceProvider.DisposeAsync().ConfigureAwait(false); + else (_serviceProvider as IDisposable)?.Dispose(); + _serviceProvider = null; + } + + internal void RegisterWindowBuilder(string id, InfiniFrameWindowBuilder builder, IServiceProvider? provider = null) { + ArgumentException.ThrowIfNullOrWhiteSpace(id); + ArgumentNullException.ThrowIfNull(builder); + RegisterWindowCore(id, null, builder, provider); + } + + private void BeginRun() { + ObjectDisposedException.ThrowIf(Volatile.Read(ref _disposed) != 0, this); + if (Interlocked.CompareExchange(ref _runState, 1, 0) != 0) + throw new InvalidOperationException("The InfiniFrame application can only be run once."); + } + + internal void ValidateWindowIntegrationTargets(IReadOnlyList? ids, string integrationName) { + lock (_gate) { + GetIntegrationTargets(ids, integrationName); + } + } + + internal void ApplyWindowIntegration( + IReadOnlyList? ids, + string integrationName, + Action configure + ) { + ArgumentNullException.ThrowIfNull(configure); + lock (_gate) { + foreach (int index in GetIntegrationTargets(ids, integrationName)) { + (string? id, Action? existing, InfiniFrameWindowBuilder? builder, IServiceProvider? provider) = _registrations[index]; + _registrations[index] = (id, target => { + existing?.Invoke(target); + configure(target); + }, builder, provider); + } + } + } + + internal void RegisterShutdownAction(Func action) { + ArgumentNullException.ThrowIfNull(action); + lock (_gate) _shutdownActions.Add(action); + } + + internal void RegisterStartupAction(Func action) { + ArgumentNullException.ThrowIfNull(action); + lock (_gate) _startupActions.Add(action); + } + + private void RegisterWindowCore( + string? id, + Action? configure, + InfiniFrameWindowBuilder? builder, + IServiceProvider? provider = null + ) { + ObjectDisposedException.ThrowIf(Volatile.Read(ref _disposed) != 0, this); + lock (_gate) { + if (_built || Volatile.Read(ref _runState) != 0) + throw new InvalidOperationException("Cannot register windows after the application run has started."); + if (id is not null && _registrations.Any(registration => registration.Id == id)) + throw new ArgumentException($"A window with id '{id}' is already registered.", nameof(id)); + _registrations.Add((id, configure, builder, provider)); + } + } + + private IReadOnlyList GetIntegrationTargets(IReadOnlyList? ids, string integrationName) { + if (ids is null || ids.Count == 0) { + if (_registrations.Count == 0) + throw new InvalidOperationException( + $"Cannot bind {integrationName}: no windows are registered. Register a window before configuring the integration."); + if (_registrations.Count != 1) + throw new InvalidOperationException( + $"Cannot bind {integrationName}: {_registrations.Count} windows are registered. Specify explicit window IDs."); + return [0]; + } + + if (ids.Any(string.IsNullOrWhiteSpace)) + throw new ArgumentException($"{integrationName} window IDs must not be null or whitespace.", nameof(ids)); + if (ids.Count != ids.Distinct(StringComparer.Ordinal).Count()) + throw new ArgumentException($"{integrationName} window IDs must not contain duplicates.", nameof(ids)); + + var targets = new List(ids.Count); + foreach (string id in ids) { + int index = _registrations.FindIndex(registration => string.Equals(registration.Id, id, StringComparison.Ordinal)); + if (index < 0) + throw new InvalidOperationException( + $"Cannot bind {integrationName}: no registered window has ID '{id}'."); + targets.Add(index); + } + return targets; + } + + private void BuildAllWindows() { + (string Id, IInfiniFrameWindow Window)[] built; + lock (_gate) { + ObjectDisposedException.ThrowIf(Volatile.Read(ref _disposed) != 0, this); + if (_built) return; + + var windows = new List<(string Id, IInfiniFrameWindow Window)>(); + try { + foreach ((string? id, Action? configure, InfiniFrameWindowBuilder? registeredBuilder, IServiceProvider? provider) in _registrations) { + InfiniFrameWindowBuilder builder = registeredBuilder ?? new InfiniFrameWindowBuilder(); + configure?.Invoke(builder); + builder.SetApplicationHandle(_nativeHandle.DangerousGetHandle()); + string windowId = id ?? Guid.NewGuid().ToString("N"); + windows.Add((windowId, builder.Build(provider ?? _serviceProvider))); + } + + foreach ((string id, IInfiniFrameWindow window) in windows) { + _windows.Add(id, window); + } + _registrations.Clear(); + _built = true; + built = [.. windows]; + } + catch { + foreach ((_, IInfiniFrameWindow window) in windows) (window as IDisposable)?.Dispose(); + throw; + } + } + + foreach ((_, IInfiniFrameWindow window) in built) + WindowCreated?.Invoke(window); + foreach ((string id, IInfiniFrameWindow window) in built) + _ = TrackNaturalWindowCloseAsync(id, window); + } + + private async Task TrackNaturalWindowCloseAsync(string id, IInfiniFrameWindow window) { + try { + await window.Features.Lifecycle.WaitForTeardownAsync().ConfigureAwait(false); + if (window is IAsyncDisposable asyncDisposable) + await asyncDisposable.DisposeAsync().ConfigureAwait(false); + else (window as IDisposable)?.Dispose(); + RemoveTrackedWindow(id, window); + } + catch (Exception exception) when (exception is not OutOfMemoryException and not StackOverflowException) { + logger.LogDebug(exception, "Could not finalize a naturally closed application window."); + } + } + + private void RemoveTrackedWindow(IInfiniFrameWindow window) { + string? id; + lock (_gate) { + id = _windows.FirstOrDefault(pair => ReferenceEquals(pair.Value, window)).Key; + if (id is null) return; + _windows.Remove(id); + } + WindowDestroyed?.Invoke(window); + } + + private void RemoveTrackedWindow(string id, IInfiniFrameWindow window) { + lock (_gate) { + if (!_windows.TryGetValue(id, out IInfiniFrameWindow? tracked) || !ReferenceEquals(tracked, window)) return; + _windows.Remove(id); + } + WindowDestroyed?.Invoke(window); + } + + private void EnsureBuilt() + { + ObjectDisposedException.ThrowIf(Volatile.Read(ref _disposed) != 0, this); + if (!_built) throw new InvalidOperationException("Windows have not been built yet. Call Run() or RunAsync() first."); + } + + private void RunNativeLoop() { + if (OperatingSystem.IsWindows()) { + InfiniFrameNativeInteropStatus status = InfiniFrameNative.ApplicationRun(_nativeHandle.DangerousGetHandle()); + if (status != InfiniFrameNativeInteropStatus.Success) + throw new InfiniFrameNativeInteropException( + InfiniFrameNative.GetLastErrorMessage() ?? "Could not run the native application."); + + // ApplicationRun drains WM_NCDESTROY before returning. If a platform + // callback was delivered after the managed owner stopped observing it, + // complete the already-observed native teardown milestone here. + foreach (IInfiniFrameWindow window in Windows.ToArray()) + window.Features.Lifecycle.CompleteTeardownAfterNativeLoop(); + return; + } + + foreach (IInfiniFrameWindow window in Windows.ToArray()) window.WaitForClose(); + } + + private static void EnsureWindowsStaThread() { + if (OperatingSystem.IsWindows() && Thread.CurrentThread.GetApartmentState() != ApartmentState.STA) + throw new InvalidOperationException("InfiniFrameApplication.Run() must be called from a Windows STA thread."); + } + + internal void AttachServiceProvider(IServiceProvider serviceProvider) => _serviceProvider = serviceProvider; + internal IServiceProvider RootServiceProvider => _serviceProvider + ?? throw new InvalidOperationException("The application service provider has not been initialized."); + + private void StopRegisteredComponents() { + Func[] actions; + lock (_gate) { + actions = _shutdownActions.ToArray(); + _shutdownActions.Clear(); + } + + foreach (Func action in actions) { + try { action().GetAwaiter().GetResult(); } + catch (Exception ex) when (ex is not OutOfMemoryException and not StackOverflowException) { + logger.LogWarning(ex, "Failed to stop an application component."); + } + } + } + + private void StartRegisteredComponents() { + Func[] actions; + lock (_gate) actions = _startupActions.ToArray(); + foreach (Func action in actions) action().GetAwaiter().GetResult(); + } + + private async Task StartRegisteredComponentsAsync() { + Func[] actions; + lock (_gate) actions = _startupActions.ToArray(); + foreach (Func action in actions) await action().ConfigureAwait(false); + } + + private async Task StopRegisteredComponentsAsync() { + Func[] actions; + lock (_gate) { + actions = _shutdownActions.ToArray(); + _shutdownActions.Clear(); + } + + foreach (Func action in actions) { + try { await action().ConfigureAwait(false); } + catch (Exception ex) when (ex is not OutOfMemoryException and not StackOverflowException) { + logger.LogWarning(ex, "Failed to stop an application component."); + } + } + } +} diff --git a/src/InfiniFrame.Application/InfiniFrameApplicationBuilder.cs b/src/InfiniFrame.Application/InfiniFrameApplicationBuilder.cs new file mode 100644 index 000000000..1936a63b8 --- /dev/null +++ b/src/InfiniFrame.Application/InfiniFrameApplicationBuilder.cs @@ -0,0 +1,99 @@ +using InfiniFrame.Window; +using Microsoft.Extensions.DependencyInjection; + +namespace InfiniFrame.Application; + +/// Composes an InfiniFrame application before it is built. +public sealed class InfiniFrameApplicationBuilder { + private readonly List> _integrations = []; + private readonly List<(string? Id, Action Configure)> _windows = []; + private string? _webView2RuntimePath; + private string? _notificationRegistrationId; + private string? _appUserModelId; + private string? _defaultNotificationIcon; + + internal InfiniFrameApplicationBuilder(string[]? args) { + Args = args ?? []; + Services = new ServiceCollection().AddLogging().AddInfiniFrame(); + } + + internal string[] Args { get; } + public IServiceCollection Services { get; } + + public InfiniFrameApplicationBuilder WithWindow(Action configure) { + ArgumentNullException.ThrowIfNull(configure); + _windows.Add((null, configure)); + return this; + } + + public InfiniFrameApplicationBuilder WithWindow(string id, Action configure) { + ArgumentException.ThrowIfNullOrWhiteSpace(id); + ArgumentNullException.ThrowIfNull(configure); + if (_windows.Any(window => string.Equals(window.Id, id, StringComparison.Ordinal))) + throw new ArgumentException($"A window with id '{id}' is already registered.", nameof(id)); + _windows.Add((id, configure)); + return this; + } + + public InfiniFrameApplicationBuilder WithWebView2RuntimePath(string path) { + ArgumentException.ThrowIfNullOrWhiteSpace(path); + _webView2RuntimePath = Path.GetFullPath(path); + return this; + } + + public InfiniFrameApplicationBuilder WithNotificationRegistrationId(string id) { + ArgumentException.ThrowIfNullOrWhiteSpace(id); + _notificationRegistrationId = id; + return this; + } + + public InfiniFrameApplicationBuilder WithAppUserModelId(string id) { + ArgumentException.ThrowIfNullOrWhiteSpace(id); + _appUserModelId = id; + return this; + } + + public InfiniFrameApplicationBuilder WithDefaultNotificationIcon(string path) { + ArgumentException.ThrowIfNullOrWhiteSpace(path); + _defaultNotificationIcon = Path.GetFullPath(path); + return this; + } + + internal void AddIntegration(Action integration) { + ArgumentNullException.ThrowIfNull(integration); + _integrations.Add(integration); + } + + public InfiniFrameApplication Build() { + InfiniFrameApplication? application = null; + IServiceProvider? serviceProvider = null; + bool serviceProviderAttached = false; + try { + application = InfiniFrameApplication.Initialize(new ApplicationConfiguration( + _webView2RuntimePath, + _notificationRegistrationId, + _appUserModelId, + _defaultNotificationIcon + )); + + Services.AddSingleton(application); + serviceProvider = Services.BuildServiceProvider(); + + foreach ((string? id, Action configure) in _windows) { + if (id is null) application.RegisterWindow(configure); + else application.RegisterWindow(id, configure); + } + application.AttachServiceProvider(serviceProvider); + serviceProviderAttached = true; + foreach (Action integration in _integrations) + integration(application); + return application; + } + catch { + application?.Dispose(); + if (!serviceProviderAttached && serviceProvider is IDisposable disposableServiceProvider) + disposableServiceProvider.Dispose(); + throw; + } + } +} diff --git a/src/InfiniFrame.Blazor/InfiniFrame.Blazor.csproj b/src/InfiniFrame.Blazor/InfiniFrame.Blazor.csproj index 78d1df882..157d0a13d 100644 --- a/src/InfiniFrame.Blazor/InfiniFrame.Blazor.csproj +++ b/src/InfiniFrame.Blazor/InfiniFrame.Blazor.csproj @@ -1,12 +1,10 @@  InfiniLore.InfiniFrame.Blazor - Library Pre-built Razor components for custom window chrome in InfiniFrame Blazor applications. Includes drag areas, window buttons, and resize thumbs. - diff --git a/src/InfiniFrame.BlazorWebView/InfiniFrame.BlazorWebView.csproj b/src/InfiniFrame.BlazorWebView/InfiniFrame.BlazorWebView.csproj index 3c9df2d66..edce4d8a4 100644 --- a/src/InfiniFrame.BlazorWebView/InfiniFrame.BlazorWebView.csproj +++ b/src/InfiniFrame.BlazorWebView/InfiniFrame.BlazorWebView.csproj @@ -1,20 +1,20 @@ InfiniLore.InfiniFrame.BlazorWebView - Library Integrates a full Blazor WebAssembly-style application into a native InfiniFrame window with no HTTP server required. The Blazor runtime runs entirely in-process. - - - - + + - + + + + diff --git a/src/InfiniFrame.BlazorWebView/InfiniFrameApplicationBlazorWebViewExtensions.cs b/src/InfiniFrame.BlazorWebView/InfiniFrameApplicationBlazorWebViewExtensions.cs new file mode 100644 index 000000000..06686c5eb --- /dev/null +++ b/src/InfiniFrame.BlazorWebView/InfiniFrameApplicationBlazorWebViewExtensions.cs @@ -0,0 +1,227 @@ +// --------------------------------------------------------------------------------------------------------------------- +// Imports +// --------------------------------------------------------------------------------------------------------------------- +using System.Reflection; +using InfiniFrame.Application; +using InfiniFrame.BlazorWebView.FileProviders; +using InfiniFrame.Security; +using InfiniFrame.StaticAssets; +using InfiniFrame.Window; +using InfiniFrame.Window.Features.WebMessaging.Handlers; +using Microsoft.AspNetCore.Components; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Options; + +namespace InfiniFrame.BlazorWebView; +// --------------------------------------------------------------------------------------------------------------------- +// Code +// --------------------------------------------------------------------------------------------------------------------- + +public sealed class InfiniFrameBlazorWebViewConfiguration { + private readonly IServiceCollection _services; + private readonly List> _windowConfigurations = []; + + internal InfiniFrameBlazorWebViewConfiguration(IServiceCollection services) { + _services = services; + ConfigureServices(); + } + + public IInfiniFrameRootComponentList RootComponents { get; } = new InfiniFrameRootComponentList(); + + public InfiniFrameBlazorWebViewConfiguration Configure(Action configure) { + ArgumentNullException.ThrowIfNull(configure); + _services.Configure(configure); + return this; + } + + public InfiniFrameBlazorWebViewConfiguration ConfigureWindow(Action configure) { + ArgumentNullException.ThrowIfNull(configure); + _windowConfigurations.Add(configure); + return this; + } + + internal void AddSingleFileProvider(IFileProvider provider) { + ArgumentNullException.ThrowIfNull(provider); + _services.AddSingleton(provider); + } + + internal void Apply(InfiniFrameApplication application, IReadOnlyList windowIds) { + IServiceProvider services = application.RootServiceProvider; + var manager = services.GetRequiredService(); + InfiniFrameBlazorAppConfiguration appConfig = services.GetService>()?.Value + ?? new InfiniFrameBlazorAppConfiguration(); + + IInfiniFrameJsComponentConfiguration? jsConfiguration = + services.GetService(); + if (jsConfiguration is not null) { + application.WindowCreated += _ => { + foreach ((Type componentType, string selector) in RootComponents) + jsConfiguration.Add(componentType, selector); + }; + } + + IDisposable? exceptionRegistration = TryRegisterUnhandledExceptionHandler(services); + application.ApplyWindowIntegration(windowIds, "BlazorWebView", windowBuilder => { + foreach (Action configure in _windowConfigurations) + configure(windowBuilder); + InfiniFrameUriSecurityPolicyRegistry.ConfigureForBuilder( + windowBuilder, + policyBuilder => policyBuilder.AddTrustedOrigin(appConfig.AppBaseUri)); + windowBuilder.StaticAssets = services.GetRequiredService().DeepCopy(); + if (!windowBuilder.EventsStore.CustomScheme.ContainsKey(InfiniFrameWebViewManager.BlazorAppScheme)) + windowBuilder.RegisterCustomSchemeHandler(InfiniFrameWebViewManager.BlazorAppScheme, manager.HandleWebRequest); + windowBuilder.RegisterWebMessageReceivedHandler(manager.HandleWebMessage); + windowBuilder.RegisterGetWebMessageHandler(); + windowBuilder.SetStartPageUrl(BuildStartupUrl(appConfig)); + }); + if (exceptionRegistration is not null) + application.RegisterShutdownAction(() => { + exceptionRegistration.Dispose(); + return Task.CompletedTask; + }); + } + + private void ConfigureServices() { + IFileProvider fileProvider = ConfigureFileProvider(null); + _services.AddOptions(); + _services + .AddInfiniFrame() + .AddScoped(sp => { + var handler = sp.GetRequiredService(); + Uri appBaseUri = sp.GetRequiredService>().Value.AppBaseUri; + return new HttpClient(handler) { BaseAddress = appBaseUri }; + }) + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddBlazorWebView() + .AddSingleton(fileProvider) + .AddSingleton(static provider => { + InfiniFrameBlazorAppConfiguration config = provider.GetService>()?.Value + ?? new InfiniFrameBlazorAppConfiguration(); + return new InfiniFrameStaticAssets { + FileProvider = provider.GetRequiredService(), + BaseUri = config.AppBaseUri.ToString(), + DefaultDocument = NormalizeHostPage(config.HostPage) + }; + }) + .AddSingleton(RootComponents) + .AddSingleton(RootComponents.JSComponents); + + _services.TryAddSingleton(); + _services.AddInfiniFrameJs(); + } + + private static IFileProvider ConfigureFileProvider(IFileProvider? fileProvider) { + if (fileProvider is not null) return fileProvider; + string baseDirectory = AppDomain.CurrentDomain.BaseDirectory; + var providers = new List(); + IFileProvider? staticWebAssets = StaticWebAssetsRuntimeFileProvider.TryCreate(baseDirectory, Assembly.GetEntryAssembly()); + if (staticWebAssets is not null) providers.Add(staticWebAssets); + string wwwroot = Path.Join(baseDirectory, "wwwroot"); + PhysicalFileProvider? physical = Directory.Exists(wwwroot) ? new PhysicalFileProvider(wwwroot) : null; + if (physical is not null) providers.Add(physical); + return providers.Count switch { + 0 => new NullFileProvider(), + 1 => providers[0], + _ => new DisposableCompositeFileProvider(providers, physical!) + }; + } + + private static string BuildStartupUrl(InfiniFrameBlazorAppConfiguration configuration) { + Uri appBaseUri = configuration.AppBaseUri; + string hostPage = NormalizeHostPage(configuration.HostPage); + return string.Equals(hostPage, "index.html", StringComparison.OrdinalIgnoreCase) + ? appBaseUri.ToString() + : new Uri(appBaseUri, hostPage).ToString(); + } + + private static string NormalizeHostPage(string? hostPage) => + !string.IsNullOrWhiteSpace(hostPage) ? hostPage.TrimStart('/') : "index.html"; + + private static IDisposable? TryRegisterUnhandledExceptionHandler(IServiceProvider services) { + bool enabled = services.GetService>()?.Value + .EnableGlobalUnhandledExceptionHandler ?? true; + if (!enabled) return null; + var source = services.GetRequiredService(); + return source.Register((_, error) => { + try { + var window = services.GetService(); + window?.Invoke(() => window.ShowMessage("Fatal exception", error.ExceptionObject.ToString())); + } + catch (ObjectDisposedException) { } + catch (InvalidOperationException) { } + }); + } +} + +public static class InfiniFrameApplicationBlazorWebViewExtensions { + public static InfiniFrameApplicationBuilder WithBlazorWebView( + this InfiniFrameApplicationBuilder builder, + Action configure + ) => builder.UseBlazorWebView(configure); + + public static InfiniFrameApplicationBuilder WithBlazorWebView( + this InfiniFrameApplicationBuilder builder, + string windowId, + Action configure + ) => builder.UseBlazorWebView(windowId, configure); + + public static InfiniFrameApplicationBuilder WithBlazorWebView( + this InfiniFrameApplicationBuilder builder, + IEnumerable windowIds, + Action configure + ) => builder.UseBlazorWebView(windowIds, configure); + + public static InfiniFrameApplicationBuilder UseBlazorWebView( + this InfiniFrameApplicationBuilder builder, + Action configure + ) => builder.UseBlazorWebView(configure, []); + + public static InfiniFrameApplicationBuilder UseBlazorWebView( + this InfiniFrameApplicationBuilder builder, + string windowId, + Action configure + ) => builder.UseBlazorWebView(configure, [windowId]); + + public static InfiniFrameApplicationBuilder UseBlazorWebView( + this InfiniFrameApplicationBuilder builder, + IEnumerable windowIds, + Action configure + ) { + ArgumentNullException.ThrowIfNull(windowIds); + return builder.UseBlazorWebView(configure, windowIds.ToArray()); + } + + public static InfiniFrameApplicationBuilder UseBlazorWebView( + this InfiniFrameApplicationBuilder builder, + Action configure, + params string[] windowIds + ) { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(configure); + ArgumentNullException.ThrowIfNull(windowIds); + if (windowIds.Length > 1) + throw new NotSupportedException("BlazorWebView can currently target only one window."); + + var configuration = new InfiniFrameBlazorWebViewConfiguration(builder.Services); + configure(configuration); + builder.Services.AddSingleton(provider => + ResolveTargetWindow(provider.GetRequiredService(), windowIds)); + builder.AddIntegration(application => { + application.ValidateWindowIntegrationTargets(windowIds, "BlazorWebView"); + configuration.Apply(application, windowIds); + }); + return builder; + } + + private static IInfiniFrameWindow ResolveTargetWindow(IInfiniFrameApplication application, IReadOnlyList windowIds) { + if (windowIds.Count > 0) return application.GetWindow(windowIds[0]); + if (application.Windows.Count == 1) return application.Windows[0]; + throw new InvalidOperationException("BlazorWebView could not resolve its target window."); + } +} diff --git a/src/InfiniFrame.BlazorWebView/InfiniFrameBlazorApp.cs b/src/InfiniFrame.BlazorWebView/InfiniFrameBlazorApp.cs deleted file mode 100644 index 2b64655ec..000000000 --- a/src/InfiniFrame.BlazorWebView/InfiniFrameBlazorApp.cs +++ /dev/null @@ -1,117 +0,0 @@ -// --------------------------------------------------------------------------------------------------------------------- -// Imports -// --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame.Utilities; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; - -namespace InfiniFrame.BlazorWebView; -// --------------------------------------------------------------------------------------------------------------------- -// Code -// --------------------------------------------------------------------------------------------------------------------- -/// -/// Represents a running Blazor application hosted in an InfiniFrame native window. -/// Manages the application lifecycle and service provider. -/// -public class InfiniFrameBlazorApp( - IServiceProvider provider, - IInfiniFrameRootComponentList rootComponents, - IInfiniFrameJsComponentConfiguration? rootComponentConfiguration = null, - IDisposable? unhandledExceptionRegistration = null -) : IInfiniFrameBlazorApp { - - private int _disposed; - /// - /// Gets the service provider for the running Blazor application. - /// - public IServiceProvider ServiceProvider { get; } = provider; - private IInfiniFrameRootComponentList RootComponents { get; } = rootComponents; - private IInfiniFrameJsComponentConfiguration? RootComponentConfiguration { get; } = rootComponentConfiguration; - private IDisposable? UnhandledExceptionRegistration { get; } = unhandledExceptionRegistration; - - // ----------------------------------------------------------------------------------------------------------------- - // Methods - // ----------------------------------------------------------------------------------------------------------------- - /// - public async Task RunAsync(CancellationToken ct = default) { - ObjectDisposedException.ThrowIf(Volatile.Read(ref _disposed) != 0, this); - - var window = ServiceProvider.GetRequiredService(); - - RegisterRootComponents(); - - try { - await window.WaitForCloseAsync(ct).ConfigureAwait(false); - } - finally { - await DisposeAsync().ConfigureAwait(false); - } - } - - /// - /// - /// This method uses synchronous-over-async patterns for disposal. It should only be called - /// from threads without a SynchronizationContext. Prefer for async contexts. - /// - public void Run() { - ObjectDisposedException.ThrowIf(Volatile.Read(ref _disposed) != 0, this); - - if (SynchronizationContext.Current is not null) { - throw new InvalidOperationException( - "Run() must be called from a thread without a SynchronizationContext to avoid deadlock during disposal. " + - "Use RunAsync() instead."); - } - - var window = ServiceProvider.GetRequiredService(); - - RegisterRootComponents(); - - try { - window.WaitForClose(); - } - finally { - DisposeAsync().AsTask().GetAwaiter().GetResult(); - } - } - - /// - /// Asynchronously disposes of the application and its service provider. - /// - /// - /// This method uses best-effort disposal: exceptions thrown during service provider - /// disposal are caught and logged but do not propagate to the caller. This prevents - /// resource cleanup failures from masking the original application shutdown. - /// - public async ValueTask DisposeAsync() { - if (Interlocked.Exchange(ref _disposed, 1) != 0) return; - - ILogger? logger = null; - - try { - logger = ServiceProvider.GetService>(); - - UnhandledExceptionRegistration?.Dispose(); - - switch (ServiceProvider) { - case IAsyncDisposable asyncDisposable: - await asyncDisposable.DisposeAsync().ConfigureAwait(false); - break; - - case IDisposable disposable: - disposable.Dispose(); - break; - } - } - catch (Exception e) when (ExceptionsUtility.IsNonFatalException(e)) { - logger?.LogError(e, "Error disposing of InfiniFrameBlazorApp"); - } - } - - private void RegisterRootComponents() { - if (RootComponentConfiguration is null) return; - - foreach ((Type, string) component in RootComponents) { - RootComponentConfiguration.Add(component.Item1, component.Item2); - } - } -} diff --git a/src/InfiniFrame.BlazorWebView/InfiniFrameBlazorAppBuilder.cs b/src/InfiniFrame.BlazorWebView/InfiniFrameBlazorAppBuilder.cs deleted file mode 100644 index b892c038f..000000000 --- a/src/InfiniFrame.BlazorWebView/InfiniFrameBlazorAppBuilder.cs +++ /dev/null @@ -1,241 +0,0 @@ -// --------------------------------------------------------------------------------------------------------------------- -// Imports -// --------------------------------------------------------------------------------------------------------------------- -using System.Reflection; -using InfiniFrame.BlazorWebView.FileProviders; -using InfiniFrame.Security; -using InfiniFrame.StaticAssets; -using Microsoft.AspNetCore.Components; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.FileProviders; -using Microsoft.Extensions.Options; - -namespace InfiniFrame.BlazorWebView; -// --------------------------------------------------------------------------------------------------------------------- -// Code -// --------------------------------------------------------------------------------------------------------------------- -/// -/// Builder for creating a Blazor application hosted in an InfiniFrame native window. -/// -public class InfiniFrameBlazorAppBuilder : IInfiniFrameBlazorAppBuilder { - - // ----------------------------------------------------------------------------------------------------------------- - // Constructors - // ----------------------------------------------------------------------------------------------------------------- - private InfiniFrameBlazorAppBuilder() {} - /// - public IInfiniFrameRootComponentList RootComponents { get; } = new InfiniFrameRootComponentList(); - /// - public IServiceCollection Services { get; } = new ServiceCollection(); - /// - public IInfiniFrameWindowBuilder WindowBuilder { get; } = InfiniFrameWindowBuilder.Create(); - - /// - /// Creates a default builder with standard configuration, command-line args, and window builder action. - /// - /// Optional command-line arguments. - /// An optional action to configure the window builder. - /// A new instance. - public static InfiniFrameBlazorAppBuilder CreateDefault( - string[]? args = null, - Action? windowBuilder = null - ) - => CreateDefault(null, args, windowBuilder); - - /// - /// Creates a default builder with standard configuration and command-line args. - /// - /// An optional file provider for static assets. - /// Optional command-line arguments. - /// An optional action to configure the window builder. - /// A new instance. - public static InfiniFrameBlazorAppBuilder CreateDefault(IFileProvider? fileProvider, string[]? args = null, Action? windowBuilder = null) { - // We don't use the args for anything right now, but we want to accept them - // here so that it shows up this way in the project templates. - var appBuilder = new InfiniFrameBlazorAppBuilder(); - IFileProvider resolvedFileProvider = ConfigureFileProvider(fileProvider); - - appBuilder.Services.AddOptions(); - - appBuilder.Services - .AddInfiniFrame() - .AddScoped(static sp => { - var handler = sp.GetRequiredService(); - return new HttpClient(handler) { BaseAddress = new Uri(InfiniFrameWebViewManager.AppBaseUri) }; - }) - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton(static provider => provider.GetRequiredService().Build(provider)) - .AddBlazorWebView() - .AddSingleton(resolvedFileProvider) - .AddSingleton(static provider => { - InfiniFrameBlazorAppConfiguration config = provider.GetService>()?.Value - ?? new InfiniFrameBlazorAppConfiguration(); - - return new InfiniFrameStaticAssets { - FileProvider = provider.GetRequiredService(), - BaseUri = config.AppBaseUri.ToString(), - DefaultDocument = NormalizeHostPage(config.HostPage) - }; - }) - .AddSingleton(appBuilder.WindowBuilder) - .AddSingleton(appBuilder.RootComponents) - .AddSingleton(appBuilder.RootComponents.JSComponents); - - appBuilder.Services.TryAddSingleton(); - - appBuilder.Services.AddInfiniFrameJs(); - appBuilder.WindowBuilder.RegisterGetWebMessageHandler(); - - windowBuilder?.Invoke(appBuilder.WindowBuilder); - - return appBuilder; - } - - /// - /// Configures the file provider to be used by the application. - /// If a custom is provided, that instance will be used. - /// Otherwise, a default provider will be configured based on the application's "wwwroot" directory. - /// - /// - /// An optional instance. - /// - /// - /// An instance of that represents either the specified file provider - /// or the default provider if none is supplied. - /// - private static IFileProvider ConfigureFileProvider(IFileProvider? fileProvider) { - if (fileProvider is not null) return fileProvider; - - string baseDirectory = AppDomain.CurrentDomain.BaseDirectory; - - var providers = new List(); - - IFileProvider? staticWebAssetsProvider = StaticWebAssetsRuntimeFileProvider.TryCreate(baseDirectory, Assembly.GetEntryAssembly()); - if (staticWebAssetsProvider is not null) providers.Add(staticWebAssetsProvider); - - string defaultWwwrootPath = Path.Join(baseDirectory, "wwwroot"); - bool hasPhysicalWwwroot = Directory.Exists(defaultWwwrootPath); - PhysicalFileProvider? physicalWwwrootProvider = hasPhysicalWwwroot - ? new PhysicalFileProvider(defaultWwwrootPath) - : null; - if (physicalWwwrootProvider is not null) providers.Add(physicalWwwrootProvider); - - return providers.Count switch { - 0 => new NullFileProvider(), - 1 => providers[0], - _ => new DisposableCompositeFileProvider(providers, physicalWwwrootProvider!) - }; - - } - - /// - /// Configures the InfiniFrame window builder action. - /// - /// The action to configure the window builder. - /// The for chaining. - public InfiniFrameBlazorAppBuilder WithInfiniFrameWindowBuilder(Action windowBuilder) { - windowBuilder.Invoke(WindowBuilder); - return this; - } - - /// - /// Builds a new using a service provider created from . - /// - /// A newly created . - public InfiniFrameBlazorApp Build() - => Build(Services.BuildServiceProvider()); - - /// - /// Builds a new using an externally supplied . - /// - /// - /// The pre-built service provider to use for resolving all application services. - /// Ownership is transferred to the returned app instance; when that app is disposed, this provider is disposed if it - /// implements - /// or . Do not dispose the same provider separately. - /// - /// A newly created . - /// - /// Calling this method more than once on the same builder instance is not supported. Each call mutates builder state - /// (for example, by registering additional scheme handlers), which can lead to duplicate registrations. - /// Create a new builder for each app instance. - /// - public InfiniFrameBlazorApp Build(IServiceProvider serviceProvider) { - ArgumentNullException.ThrowIfNull(serviceProvider); - - var manager = serviceProvider.GetRequiredService(); - InfiniFrameBlazorAppConfiguration appConfig = serviceProvider.GetService>()?.Value - ?? new InfiniFrameBlazorAppConfiguration(); - InfiniFrameUriSecurityPolicyRegistry.ConfigureForBuilder( - WindowBuilder, - configure: policyBuilder => policyBuilder.AddTrustedOrigin(appConfig.AppBaseUri)); - string startupUrl = BuildStartupUrl(appConfig); - var staticAssets = serviceProvider.GetRequiredService(); - - WindowBuilder.StaticAssets = staticAssets.DeepCopy(); - - if (!WindowBuilder.EventsStore.CustomScheme.ContainsKey(InfiniFrameWebViewManager.BlazorAppScheme)) { - WindowBuilder.RegisterCustomSchemeHandler(InfiniFrameWebViewManager.BlazorAppScheme, manager.HandleWebRequest); - } - - WindowBuilder.SetStartPageUrl(startupUrl); - - IDisposable? unhandledExceptionRegistration = TryRegisterUnhandledExceptionHandler(serviceProvider); - - return new InfiniFrameBlazorApp( - serviceProvider, - serviceProvider.GetRequiredService(), - serviceProvider.GetService(), - unhandledExceptionRegistration - ); - } - - private static string BuildStartupUrl(InfiniFrameBlazorAppConfiguration configuration) { - Uri appBaseUri = configuration.AppBaseUri; - string hostPage = NormalizeHostPage(configuration.HostPage); - - return string.Equals(hostPage, "index.html", StringComparison.OrdinalIgnoreCase) - ? appBaseUri.ToString() - : new Uri(appBaseUri, hostPage).ToString(); - } - - private static string NormalizeHostPage(string? hostPage) - => !string.IsNullOrWhiteSpace(hostPage) - ? hostPage.TrimStart('/') - : "index.html"; - - private static IDisposable? TryRegisterUnhandledExceptionHandler(IServiceProvider serviceProvider) { - bool enableGlobalUnhandledExceptionHandler = serviceProvider.GetService>()? - .Value.EnableGlobalUnhandledExceptionHandler ?? true; - - if (!enableGlobalUnhandledExceptionHandler) return null; - - var exceptionSource = serviceProvider.GetRequiredService(); - - return exceptionSource.Register((_, error) => { - try { - var window = serviceProvider.GetService(); - - // Only interact if safe - window?.Invoke(() => { - window.ShowMessage( - "Fatal exception", - error.ExceptionObject.ToString() - ); - }); - } - catch (ObjectDisposedException) { - // Window already closed; nothing to report. - } - catch (InvalidOperationException) { - // Service not available; nothing to report. - } - }); - } -} diff --git a/src/InfiniFrame.BlazorWebView/InfiniFrameBlazorAppConfiguration.cs b/src/InfiniFrame.BlazorWebView/InfiniFrameBlazorAppConfiguration.cs index 23923fe1c..8447ec080 100644 --- a/src/InfiniFrame.BlazorWebView/InfiniFrameBlazorAppConfiguration.cs +++ b/src/InfiniFrame.BlazorWebView/InfiniFrameBlazorAppConfiguration.cs @@ -34,8 +34,6 @@ public class InfiniFrameBlazorAppConfiguration { /// /// Gets or sets how outbound messages are handled when is reached. /// The default rejects the new message, which provides immediate backpressure to the non-awaitable Blazor API. - /// Note: The current implementation always uses TryWrite (non-blocking), so this setting only controls - /// diagnostic logging and is reserved for future use with blocking write paths. /// public BoundedChannelFullMode WebMessageQueueFullMode { get; set; } = BoundedChannelFullMode.DropWrite; } diff --git a/src/InfiniFrame.BlazorWebView/InfiniFrameWebViewManager.cs b/src/InfiniFrame.BlazorWebView/InfiniFrameWebViewManager.cs index 7dd3033ba..be14683e8 100644 --- a/src/InfiniFrame.BlazorWebView/InfiniFrameWebViewManager.cs +++ b/src/InfiniFrame.BlazorWebView/InfiniFrameWebViewManager.cs @@ -40,7 +40,8 @@ public class InfiniFrameWebViewManager : WebViewManager, IInfiniFrameWebViewMana private readonly Task _messagePumpTask; private readonly int _messageQueueCapacity; private readonly BoundedChannelFullMode _messageQueueFullMode; - private readonly IInfiniFrameUriSecurityPolicy _uriSecurityPolicy; + private readonly Uri _appBaseUri; + private readonly IInfiniFrameUriSecurityPolicy _fallbackUriSecurityPolicy; private int _disposeStarted; private int _disposed; @@ -50,7 +51,6 @@ public class InfiniFrameWebViewManager : WebViewManager, IInfiniFrameWebViewMana /// /// Initializes a new instance of the class. /// - /// The window builder for configuring the native window. /// The service provider for dependency injection. /// The Blazor dispatcher for thread marshalling. /// The file provider for serving static assets. @@ -58,7 +58,6 @@ public class InfiniFrameWebViewManager : WebViewManager, IInfiniFrameWebViewMana /// The Blazor application configuration. /// The logger public InfiniFrameWebViewManager( - IInfiniFrameWindowBuilder builder, IServiceProvider provider, Dispatcher dispatcher, IFileProvider fileProvider, @@ -69,6 +68,13 @@ ILogger logger : base(provider, dispatcher, config.Value.AppBaseUri, fileProvider, jsComponents, config.Value.HostPage) { _logger = logger; InfiniFrameBlazorAppConfiguration configuration = config.Value; + if (configuration.AppBaseUri is null + || !configuration.AppBaseUri.IsAbsoluteUri + || string.IsNullOrWhiteSpace(configuration.AppBaseUri.Scheme) + || string.IsNullOrWhiteSpace(configuration.AppBaseUri.Host)) { + throw new ArgumentException("AppBaseUri must be an absolute URI with a scheme and host.", nameof(configuration.AppBaseUri)); + } + _appBaseUri = configuration.AppBaseUri; if (configuration.WebMessageQueueCapacity <= 0) { throw new ArgumentOutOfRangeException( nameof(configuration.WebMessageQueueCapacity), @@ -76,34 +82,25 @@ ILogger logger "The WebView message queue capacity must be positive."); } + // TryWrite cannot report which item DropWrite discarded. Use Wait so the non-awaitable + // producer receives a false result and the loss is observable through diagnostics. + BoundedChannelFullMode effectiveFullMode = configuration.WebMessageQueueFullMode == BoundedChannelFullMode.DropWrite + ? BoundedChannelFullMode.Wait + : configuration.WebMessageQueueFullMode; _channel = Channel.CreateBounded(new BoundedChannelOptions(configuration.WebMessageQueueCapacity) { SingleReader = true, SingleWriter = false, - FullMode = configuration.WebMessageQueueFullMode, + FullMode = effectiveFullMode, AllowSynchronousContinuations = false }); _messageQueueCapacity = configuration.WebMessageQueueCapacity; - _messageQueueFullMode = configuration.WebMessageQueueFullMode; - _uriSecurityPolicy = InfiniFrameUriSecurityPolicyRegistry - .GetForBuilder(builder) + _messageQueueFullMode = effectiveFullMode; + _fallbackUriSecurityPolicy = InfiniFrameUriSecurityPolicy.Default .WithTrustedOrigin(configuration.AppBaseUri); // ReSharper disable once ConvertClosureToMethodGroup LazyWindow = new Lazy(() => provider.GetRequiredService()); - builder.RegisterWebMessageReceivedHandler((_, message, origin) => { - if (IsDisposingOrDisposed) return; - - _logger.LogTrace("Web message callback received from native. Origin: {Origin}, Length: {Length}", origin, message.Length); - - try { - HandleWebMessage((message, origin)); - } - catch (Exception ex) when (ExceptionsUtility.IsNonFatalException(ex)) { - _logger.LogWarning(ex, "Unhandled exception while handling native web message callback."); - } - }); - _messagePumpTask = MessagePump(); _logger.LogDebug( "Started WebView message pump. QueueCapacity: {QueueCapacity}, FullMode: {FullMode}", @@ -135,7 +132,8 @@ ILogger logger return default; } - if (!_uriSecurityPolicy.IsNavigationSchemeAllowed(requestUri.Scheme)) { + IInfiniFrameUriSecurityPolicy uriSecurityPolicy = GetUriSecurityPolicy(infiniFrameWindow); + if (!uriSecurityPolicy.IsNavigationSchemeAllowed(requestUri.Scheme)) { _logger.LogWarning( "Rejected web request due to disallowed URI scheme. Scheme: {Scheme}, Url: {Url}", requestUri.Scheme, @@ -143,11 +141,11 @@ ILogger logger return default; } - if (!_uriSecurityPolicy.IsTrustedOrigin(requestUri)) { + if (!uriSecurityPolicy.IsTrustedOrigin(requestUri)) { _logger.LogWarning( "Rejected web request due to untrusted origin. RequestOrigin: {RequestOrigin}, TrustedOrigins: {TrustedOrigins}", requestUri, - _uriSecurityPolicy.TrustedOrigins); + uriSecurityPolicy.TrustedOrigins); return default; } @@ -183,20 +181,38 @@ ILogger logger // ----------------------------------------------------------------------------------------------------------------- // Web message handling // ----------------------------------------------------------------------------------------------------------------- - private void HandleWebMessage((string Message, string? Origin) state) { + /// + public void HandleWebMessage(IInfiniFrameWindow window, string message, string? origin) { + ArgumentNullException.ThrowIfNull(window); + ArgumentNullException.ThrowIfNull(message); + + if (IsDisposingOrDisposed) return; + + _logger.LogTrace("Web message callback received from native. Origin: {Origin}, Length: {Length}", origin, message.Length); + + try { + HandleWebMessageCore(window, message, origin); + } + catch (Exception ex) when (ExceptionsUtility.IsNonFatalException(ex)) { + _logger.LogWarning(ex, "Unhandled exception while handling native web message callback."); + } + } + + private void HandleWebMessageCore(IInfiniFrameWindow window, string message, string? origin) { if (IsDisposingOrDisposed) return; Uri? messageOriginUrl; - if (!string.IsNullOrWhiteSpace(state.Origin)) { - if (!Uri.TryCreate(state.Origin, UriKind.Absolute, out messageOriginUrl)) { + if (!string.IsNullOrWhiteSpace(origin)) { + if (!Uri.TryCreate(origin, UriKind.Absolute, out messageOriginUrl)) { _logger.LogWarning( "Rejected web message because origin parsing failed. Origin: {Origin}", - state.Origin); + origin); return; } } - else if (Uri.TryCreate(AppBaseUri, UriKind.Absolute, out Uri? fallback)) { + else if (_appBaseUri.IsAbsoluteUri) { + Uri fallback = _appBaseUri; messageOriginUrl = fallback; _logger.LogDebug( @@ -209,11 +225,12 @@ private void HandleWebMessage((string Message, string? Origin) state) { return; } - if (!_uriSecurityPolicy.IsTrustedOrigin(messageOriginUrl)) { + IInfiniFrameUriSecurityPolicy uriSecurityPolicy = GetUriSecurityPolicy(window); + if (!uriSecurityPolicy.IsTrustedOrigin(messageOriginUrl)) { _logger.LogWarning( "Rejected web message due to origin mismatch. Origin: {MessageOrigin}, TrustedOrigins: {TrustedOrigins}", messageOriginUrl, - _uriSecurityPolicy.TrustedOrigins); + uriSecurityPolicy.TrustedOrigins); return; } @@ -221,9 +238,14 @@ private void HandleWebMessage((string Message, string? Origin) state) { // messages because the pump synchronously invokes that same thread to send responses. if (IsDisposingOrDisposed) return; - MessageReceived(messageOriginUrl, state.Message); + MessageReceived(messageOriginUrl, message); } + private IInfiniFrameUriSecurityPolicy GetUriSecurityPolicy(IInfiniFrameWindow? window) => + window is null + ? _fallbackUriSecurityPolicy + : InfiniFrameUriSecurityPolicyRegistry.GetForWindow(window); + // ----------------------------------------------------------------------------------------------------------------- // Navigation // ----------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame.Js/InfiniFrame.Js.csproj b/src/InfiniFrame.Js/InfiniFrame.Js.csproj index f114d9cbe..90834938e 100644 --- a/src/InfiniFrame.Js/InfiniFrame.Js.csproj +++ b/src/InfiniFrame.Js/InfiniFrame.Js.csproj @@ -2,9 +2,6 @@ InfiniLore.InfiniFrame.Js - Library - - true JavaScript interop utilities for InfiniFrame Blazor applications. Provides pointer capture helpers and built-in window management message handlers. $([System.IO.Path]::GetFullPath('$(MSBuildProjectDirectory)/../..')) @@ -15,6 +12,12 @@ $(MSBuildProjectDirectory)/obj/frontend-build.stamp + + + + + + @@ -67,16 +67,16 @@ + Exclude="Native/build/**/*;Native/packages/**/*;Native/src/Embedded/InfiniFrameJs/**/*" /> + Exclude="Native/build/**/*;Native/packages/**/*;Native/src/Embedded/InfiniFrameJs/**/*" /> + Exclude="Native/build/**/*;Native/packages/**/*;Native/src/Embedded/InfiniFrameJs/**/*" /> + Exclude="Native/build/**/*;Native/packages/**/*" /> + Exclude="Native/build/**/*;Native/packages/**/*" /> diff --git a/src/InfiniFrame.NativeBridge/InfiniFrame.NativeBridge.csproj b/src/InfiniFrame.NativeBridge/InfiniFrame.NativeBridge.csproj index e129f9500..296b9a934 100644 --- a/src/InfiniFrame.NativeBridge/InfiniFrame.NativeBridge.csproj +++ b/src/InfiniFrame.NativeBridge/InfiniFrame.NativeBridge.csproj @@ -2,15 +2,15 @@ InfiniLore.InfiniFrame.NativeBridge - Library true - true true C++ native bridge layer for InfiniFrame. Provides the P/Invoke interop between .NET and the native window implementations (WebView2, WebKitGTK, WKWebView). + + diff --git a/src/InfiniFrame.NativeBridge/Managed/Handles/NativeApplicationHandle.cs b/src/InfiniFrame.NativeBridge/Managed/Handles/NativeApplicationHandle.cs new file mode 100644 index 000000000..ad6defa6a --- /dev/null +++ b/src/InfiniFrame.NativeBridge/Managed/Handles/NativeApplicationHandle.cs @@ -0,0 +1,13 @@ +using Microsoft.Win32.SafeHandles; + +namespace InfiniFrame.NativeBridge.Handles; + +/// Owns a native InfiniFrame application instance. +public sealed class NativeApplicationHandle : SafeHandleZeroOrMinusOneIsInvalid { + internal NativeApplicationHandle(IntPtr handle) : base(true) => SetHandle(handle); + + protected override bool ReleaseHandle() { + InfiniFrameNativeInteropStatus status = InfiniFrameNative.ApplicationDestructor(handle); + return status == InfiniFrameNativeInteropStatus.Success; + } +} diff --git a/src/InfiniFrame.NativeBridge/Managed/NativeApi/Exports/InfiniFrameNative.Lifecycle.cs b/src/InfiniFrame.NativeBridge/Managed/NativeApi/Exports/InfiniFrameNative.Lifecycle.cs index d1176d5c3..d373e9bda 100644 --- a/src/InfiniFrame.NativeBridge/Managed/NativeApi/Exports/InfiniFrameNative.Lifecycle.cs +++ b/src/InfiniFrame.NativeBridge/Managed/NativeApi/Exports/InfiniFrameNative.Lifecycle.cs @@ -3,13 +3,60 @@ // --------------------------------------------------------------------------------------------------------------------- using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Application; +using InfiniFrame.NativeBridge.Parameters.Window; namespace InfiniFrame.NativeBridge; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- public partial class InfiniFrameNative { + [LibraryImport(ArtifactManifest.NativeLibraryName, EntryPoint = "InfiniFrameNativeApplication_ctor", SetLastError = true)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + private static partial InfiniFrameNativeInteropStatus ApplicationConstructorNative(out IntPtr value); + + internal static InfiniFrameNativeInteropStatus ApplicationConstructor(out IntPtr value) + => ApplicationConstructorNative(out value); + + [LibraryImport(ArtifactManifest.NativeLibraryName, EntryPoint = "InfiniFrameNativeApplication_Register", SetLastError = true)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + internal static partial InfiniFrameNativeInteropStatus ApplicationRegister(IntPtr instance); + + [LibraryImport(ArtifactManifest.NativeLibraryName, EntryPoint = "InfiniFrameNativeApplication_Configure", SetLastError = true)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + private static partial InfiniFrameNativeInteropStatus ApplicationConfigureNative(IntPtr instance, IntPtr parameters); + + internal static InfiniFrameNativeInteropStatus ApplicationConfigure( + IntPtr instance, + in InfiniFrameNativeApplicationParameters parameters + ) { + var marshaller = new InfiniFrameNativeApplicationParametersMarshaller.ManagedToUnmanagedIn(); + marshaller.FromManaged(parameters); + IntPtr unmanagedPtr = IntPtr.Zero; + + try { + unmanagedPtr = Marshal.AllocHGlobal(Marshal.SizeOf()); + Marshal.StructureToPtr(marshaller.ToUnmanaged(), unmanagedPtr, false); + return ApplicationConfigureNative(instance, unmanagedPtr); + } + finally { + if (unmanagedPtr != IntPtr.Zero) Marshal.FreeHGlobal(unmanagedPtr); + marshaller.Free(); + } + } + + [LibraryImport(ArtifactManifest.NativeLibraryName, EntryPoint = "InfiniFrameNativeApplication_Run", SetLastError = true)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + internal static partial InfiniFrameNativeInteropStatus ApplicationRun(IntPtr instance); + + [LibraryImport(ArtifactManifest.NativeLibraryName, EntryPoint = "InfiniFrameNativeApplication_Shutdown", SetLastError = true)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + internal static partial InfiniFrameNativeInteropStatus ApplicationShutdown(IntPtr instance); + + [LibraryImport(ArtifactManifest.NativeLibraryName, EntryPoint = "InfiniFrameNativeApplication_dtor")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + internal static partial InfiniFrameNativeInteropStatus ApplicationDestructor(IntPtr instance); + /// /// Creates a new native window instance with the specified parameters. /// @@ -21,16 +68,16 @@ public partial class InfiniFrameNative { private static partial InfiniFrameNativeInteropStatus ConstructorNative(IntPtr parameters, out IntPtr value); internal static InfiniFrameNativeInteropStatus Constructor( - in InfiniFrameNativeParameters parameters, + in InfiniFrameNativeWindowParameters parameters, out IntPtr value ) { - var marshaller = new InfiniFrameNativeParametersMarshaller.ManagedToUnmanagedIn(); + var marshaller = new InfiniFrameNativeWindowParametersMarshaller.ManagedToUnmanagedIn(); marshaller.FromManaged(parameters); var unmanaged = marshaller.ToUnmanaged(); IntPtr unmanagedPtr = IntPtr.Zero; try { - unmanagedPtr = Marshal.AllocHGlobal(Marshal.SizeOf()); + unmanagedPtr = Marshal.AllocHGlobal(Marshal.SizeOf()); Marshal.StructureToPtr(unmanaged, unmanagedPtr, false); return ConstructorNative(unmanagedPtr, out value); } @@ -71,6 +118,10 @@ out IntPtr value [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] internal static partial InfiniFrameNativeInteropStatus SetReadyCallback(IntPtr instance, ContextAction callback, IntPtr context); + [LibraryImport(ArtifactManifest.NativeLibraryName, EntryPoint = "InfiniFrameNative_SetReadyFailureCallback", SetLastError = true)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + internal static partial InfiniFrameNativeInteropStatus SetReadyFailureCallback(IntPtr instance, ContextAction callback, IntPtr context); + [LibraryImport(ArtifactManifest.NativeLibraryName, EntryPoint = "InfiniFrameNative_SetTeardownCallback", SetLastError = true)] [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] internal static partial InfiniFrameNativeInteropStatus SetTeardownCallback(IntPtr instance, ContextAction callback, IntPtr context); diff --git a/src/InfiniFrame.NativeBridge/Managed/NativeApi/Testing/InfiniFrameNative.Testing.cs b/src/InfiniFrame.NativeBridge/Managed/NativeApi/Testing/InfiniFrameNative.Testing.cs index 708f605f4..cf09c89a1 100644 --- a/src/InfiniFrame.NativeBridge/Managed/NativeApi/Testing/InfiniFrameNative.Testing.cs +++ b/src/InfiniFrame.NativeBridge/Managed/NativeApi/Testing/InfiniFrameNative.Testing.cs @@ -3,7 +3,8 @@ // --------------------------------------------------------------------------------------------------------------------- using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Application; +using InfiniFrame.NativeBridge.Parameters.Window; namespace InfiniFrame.NativeBridge; // --------------------------------------------------------------------------------------------------------------------- @@ -62,21 +63,21 @@ out int valid private static partial InfiniFrameNativeInteropStatus IsColorSchemeChangeNative(IntPtr lParam, out int result); /// - /// Returns a native pointer to a newly allocated InfiniFrameInitParams clone. + /// Returns a native pointer to a newly allocated InfiniFrameWindowInitParams clone. /// Ownership is transferred to managed caller, which must call exactly once. /// /// The parameters to clone. /// The native pointer to the cloned parameters. /// A status code indicating success or failure. - internal static InfiniFrameNativeInteropStatus NativeParametersReturnAsIsPtr(ref InfiniFrameNativeParameters parameters, out IntPtr newParametersPtr) { - var marshaller = new InfiniFrameNativeParametersMarshaller.ManagedToUnmanagedIn(); + internal static InfiniFrameNativeInteropStatus NativeParametersReturnAsIsPtr(ref InfiniFrameNativeWindowParameters parameters, out IntPtr newParametersPtr) { + var marshaller = new InfiniFrameNativeWindowParametersMarshaller.ManagedToUnmanagedIn(); marshaller.FromManaged(parameters); var unmanaged = marshaller.ToUnmanaged(); InfiniFrameNativeInteropStatus status; IntPtr unmanagedPtr = IntPtr.Zero; try { - unmanagedPtr = Marshal.AllocHGlobal(Marshal.SizeOf()); + unmanagedPtr = Marshal.AllocHGlobal(Marshal.SizeOf()); Marshal.StructureToPtr(unmanaged, unmanagedPtr, false); status = NativeParametersReturnAsIsNative(unmanagedPtr, out newParametersPtr); } @@ -91,6 +92,41 @@ internal static InfiniFrameNativeInteropStatus NativeParametersReturnAsIsPtr(ref return status; } + [LibraryImport(ArtifactManifest.NativeLibraryName, EntryPoint = "InfiniFrameNativeTests_NativeApplicationParametersReturnAsIs", SetLastError = true)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + private static partial InfiniFrameNativeInteropStatus NativeApplicationParametersReturnAsIsNative(IntPtr parameters, out IntPtr newParameters); + + internal static InfiniFrameNativeInteropStatus NativeApplicationParametersReturnAsIsPtr( + ref InfiniFrameNativeApplicationParameters parameters, + out IntPtr newParametersPtr + ) { + var marshaller = new InfiniFrameNativeApplicationParametersMarshaller.ManagedToUnmanagedIn(); + marshaller.FromManaged(parameters); + var unmanaged = marshaller.ToUnmanaged(); + InfiniFrameNativeInteropStatus status; + IntPtr unmanagedPtr = IntPtr.Zero; + + try { + unmanagedPtr = Marshal.AllocHGlobal(Marshal.SizeOf()); + Marshal.StructureToPtr(unmanaged, unmanagedPtr, false); + status = NativeApplicationParametersReturnAsIsNative(unmanagedPtr, out newParametersPtr); + } + finally { + if (unmanagedPtr != IntPtr.Zero) Marshal.FreeHGlobal(unmanagedPtr); + marshaller.Free(); + } + + if (newParametersPtr == IntPtr.Zero) throw new InvalidOperationException("Native function returned null pointer"); + return status; + } + + internal static InfiniFrameNativeInteropStatus FreeApplicationParameters(IntPtr parameters) + => FreeApplicationParametersNative(parameters); + + [LibraryImport(ArtifactManifest.NativeLibraryName, EntryPoint = "InfiniFrameNativeTests_FreeApplicationParameters", SetLastError = true)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + private static partial InfiniFrameNativeInteropStatus FreeApplicationParametersNative(IntPtr parameters); + /// /// Frees init parameters that were allocated by native code during testing. /// diff --git a/src/InfiniFrame.NativeBridge/Managed/Parameters/Application/InfiniFrameNativeApplicationParameters.cs b/src/InfiniFrame.NativeBridge/Managed/Parameters/Application/InfiniFrameNativeApplicationParameters.cs new file mode 100644 index 000000000..324ff7dd5 --- /dev/null +++ b/src/InfiniFrame.NativeBridge/Managed/Parameters/Application/InfiniFrameNativeApplicationParameters.cs @@ -0,0 +1,29 @@ +// --------------------------------------------------------------------------------------------------------------------- +// Imports +// --------------------------------------------------------------------------------------------------------------------- +using System.Runtime.InteropServices; + +namespace InfiniFrame.NativeBridge.Parameters.Application; +// --------------------------------------------------------------------------------------------------------------------- +// Code +// --------------------------------------------------------------------------------------------------------------------- +/// +/// Process-wide configuration passed to the native InfiniFrame application. +/// Field order is part of the managed/native ABI. Append fields before only. +/// +[StructLayout(LayoutKind.Sequential)] +public struct InfiniFrameNativeApplicationParameters() { + [MarshalAs(UnmanagedType.LPUTF8Str)] + internal string? WebView2RuntimePath; + + [MarshalAs(UnmanagedType.LPUTF8Str)] + internal string? NotificationRegistrationId; + + [MarshalAs(UnmanagedType.LPUTF8Str)] + internal string? AppUserModelId; + + [MarshalAs(UnmanagedType.LPUTF8Str)] + internal string? DefaultNotificationIcon; + + internal readonly int Size = Marshal.SizeOf(); +} diff --git a/src/InfiniFrame.NativeBridge/Managed/Parameters/Application/InfiniFrameNativeApplicationParametersEqualityComparer.cs b/src/InfiniFrame.NativeBridge/Managed/Parameters/Application/InfiniFrameNativeApplicationParametersEqualityComparer.cs new file mode 100644 index 000000000..48e995cc8 --- /dev/null +++ b/src/InfiniFrame.NativeBridge/Managed/Parameters/Application/InfiniFrameNativeApplicationParametersEqualityComparer.cs @@ -0,0 +1,34 @@ +// --------------------------------------------------------------------------------------------------------------------- +// Imports +// --------------------------------------------------------------------------------------------------------------------- +namespace InfiniFrame.NativeBridge.Parameters.Application; +// --------------------------------------------------------------------------------------------------------------------- +// Code +// --------------------------------------------------------------------------------------------------------------------- +/// +/// Compares native application parameters by their configuration values and ABI size. +/// +internal sealed class InfiniFrameNativeApplicationParametersEqualityComparer + : IEqualityComparer { + internal static readonly InfiniFrameNativeApplicationParametersEqualityComparer Instance = new(); + + private InfiniFrameNativeApplicationParametersEqualityComparer() {} + + public bool Equals( + InfiniFrameNativeApplicationParameters x, + InfiniFrameNativeApplicationParameters y + ) => x.WebView2RuntimePath == y.WebView2RuntimePath + && x.NotificationRegistrationId == y.NotificationRegistrationId + && x.AppUserModelId == y.AppUserModelId + && x.DefaultNotificationIcon == y.DefaultNotificationIcon + && x.Size == y.Size; + + public int GetHashCode(InfiniFrameNativeApplicationParameters obj) + => HashCode.Combine( + obj.WebView2RuntimePath, + obj.NotificationRegistrationId, + obj.AppUserModelId, + obj.DefaultNotificationIcon, + obj.Size + ); +} diff --git a/src/InfiniFrame.NativeBridge/Managed/Parameters/Application/InfiniFrameNativeApplicationParametersMarshaller.cs b/src/InfiniFrame.NativeBridge/Managed/Parameters/Application/InfiniFrameNativeApplicationParametersMarshaller.cs new file mode 100644 index 000000000..f14ee006b --- /dev/null +++ b/src/InfiniFrame.NativeBridge/Managed/Parameters/Application/InfiniFrameNativeApplicationParametersMarshaller.cs @@ -0,0 +1,52 @@ +// --------------------------------------------------------------------------------------------------------------------- +// Imports +// --------------------------------------------------------------------------------------------------------------------- +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace InfiniFrame.NativeBridge.Parameters.Application; +// --------------------------------------------------------------------------------------------------------------------- +// Code +// --------------------------------------------------------------------------------------------------------------------- +[CustomMarshaller( + typeof(InfiniFrameNativeApplicationParameters), + MarshalMode.ManagedToUnmanagedIn, + typeof(ManagedToUnmanagedIn) +)] +internal static class InfiniFrameNativeApplicationParametersMarshaller { + [StructLayout(LayoutKind.Sequential)] + internal struct Unmanaged { + internal IntPtr WebView2RuntimePath; + internal IntPtr NotificationRegistrationId; + internal IntPtr AppUserModelId; + internal IntPtr DefaultNotificationIcon; + internal int Size; + } + + internal ref struct ManagedToUnmanagedIn { + private Unmanaged _unmanaged; + + public void FromManaged(InfiniFrameNativeApplicationParameters managed) { + _unmanaged = new Unmanaged { + WebView2RuntimePath = ToUtf8Ptr(managed.WebView2RuntimePath), + NotificationRegistrationId = ToUtf8Ptr(managed.NotificationRegistrationId), + AppUserModelId = ToUtf8Ptr(managed.AppUserModelId), + DefaultNotificationIcon = ToUtf8Ptr(managed.DefaultNotificationIcon), + Size = managed.Size + }; + } + + public Unmanaged ToUnmanaged() => _unmanaged; + + public void Free() { + Marshal.FreeCoTaskMem(_unmanaged.WebView2RuntimePath); + Marshal.FreeCoTaskMem(_unmanaged.NotificationRegistrationId); + Marshal.FreeCoTaskMem(_unmanaged.AppUserModelId); + Marshal.FreeCoTaskMem(_unmanaged.DefaultNotificationIcon); + } + + private static IntPtr ToUtf8Ptr(string? value) => value is null + ? IntPtr.Zero + : Marshal.StringToCoTaskMemUTF8(value); + } +} diff --git a/src/InfiniFrame.NativeBridge/Managed/Parameters/Application/InfiniFrameNativeApplicationParametersValidator.cs b/src/InfiniFrame.NativeBridge/Managed/Parameters/Application/InfiniFrameNativeApplicationParametersValidator.cs new file mode 100644 index 000000000..28f94b782 --- /dev/null +++ b/src/InfiniFrame.NativeBridge/Managed/Parameters/Application/InfiniFrameNativeApplicationParametersValidator.cs @@ -0,0 +1,20 @@ +// --------------------------------------------------------------------------------------------------------------------- +// Imports +// --------------------------------------------------------------------------------------------------------------------- +using System.Runtime.InteropServices; +using FluentValidation; + +namespace InfiniFrame.NativeBridge.Parameters.Application; +// --------------------------------------------------------------------------------------------------------------------- +// Code +// --------------------------------------------------------------------------------------------------------------------- +/// +/// Validates instances. +/// +public sealed class InfiniFrameNativeApplicationParametersValidator + : AbstractValidator { + public InfiniFrameNativeApplicationParametersValidator() { + RuleFor(parameters => parameters.Size) + .Equal(Marshal.SizeOf()); + } +} diff --git a/src/InfiniFrame.NativeBridge/Managed/Parameters/CustomSchemeNameMemory.cs b/src/InfiniFrame.NativeBridge/Managed/Parameters/Window/CustomSchemeNameMemory.cs similarity index 98% rename from src/InfiniFrame.NativeBridge/Managed/Parameters/CustomSchemeNameMemory.cs rename to src/InfiniFrame.NativeBridge/Managed/Parameters/Window/CustomSchemeNameMemory.cs index 84123a977..124840b18 100644 --- a/src/InfiniFrame.NativeBridge/Managed/Parameters/CustomSchemeNameMemory.cs +++ b/src/InfiniFrame.NativeBridge/Managed/Parameters/Window/CustomSchemeNameMemory.cs @@ -3,7 +3,7 @@ // --------------------------------------------------------------------------------------------------------------------- using System.Runtime.InteropServices; -namespace InfiniFrame.NativeBridge.Parameters; +namespace InfiniFrame.NativeBridge.Parameters.Window; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeParameters.cs b/src/InfiniFrame.NativeBridge/Managed/Parameters/Window/InfiniFrameNativeWindowParameters.cs similarity index 95% rename from src/InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeParameters.cs rename to src/InfiniFrame.NativeBridge/Managed/Parameters/Window/InfiniFrameNativeWindowParameters.cs index 2c20201e5..ec6a6fdda 100644 --- a/src/InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeParameters.cs +++ b/src/InfiniFrame.NativeBridge/Managed/Parameters/Window/InfiniFrameNativeWindowParameters.cs @@ -4,7 +4,7 @@ using System.Runtime.InteropServices; using InfiniFrame.NativeBridge.Delegates; -namespace InfiniFrame.NativeBridge.Parameters; +namespace InfiniFrame.NativeBridge.Parameters.Window; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -16,7 +16,7 @@ namespace InfiniFrame.NativeBridge.Parameters; /// Passed to the native layer as a sequentially laid-out struct. /// [StructLayout(LayoutKind.Sequential)] -public struct InfiniFrameNativeParameters() { +public struct InfiniFrameNativeWindowParameters() { // Content strings /// @@ -70,20 +70,6 @@ public struct InfiniFrameNativeParameters() { [MarshalAs(UnmanagedType.LPUTF8Str)] internal string? BrowserControlInitParameters; - /// - /// WINDOWS ONLY: OPTIONAL: Path to an extracted fixed-version WebView2 runtime used when the window is created. - /// - [MarshalAs(UnmanagedType.LPUTF8Str)] - internal string? WebView2RuntimePath; - - ///WINDOWS: OPTIONAL: Registers the application for toast notifications. If not provided, use Window Title. - [MarshalAs(UnmanagedType.LPUTF8Str)] - internal string? NotificationRegistrationId; - - ///WINDOWS: OPTIONAL: Explicit application identity used by the taskbar for grouping and pinning. - [MarshalAs(UnmanagedType.LPUTF8Str)] - internal string? WindowsAppUserModelId; - /// /// OPTIONAL: Default icon path applied to notifications when IconPath is not specified. /// @@ -103,6 +89,9 @@ public struct InfiniFrameNativeParameters() { /// internal IntPtr NativeParent; + /// Process-scoped application owner for this native window. + internal IntPtr ApplicationInstance; + // Event callbacks ///Set by InfiniFrameOptionsBuilder @@ -401,5 +390,5 @@ public struct InfiniFrameNativeParameters() { /// construction. /// [MarshalAs(UnmanagedType.I4)] - internal readonly int Size = Marshal.SizeOf(); + internal readonly int Size = Marshal.SizeOf(); } diff --git a/src/InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeParametersEqualityComparer.cs b/src/InfiniFrame.NativeBridge/Managed/Parameters/Window/InfiniFrameNativeWindowParametersEqualityComparer.cs similarity index 88% rename from src/InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeParametersEqualityComparer.cs rename to src/InfiniFrame.NativeBridge/Managed/Parameters/Window/InfiniFrameNativeWindowParametersEqualityComparer.cs index a2efd00cb..9d821d1fc 100644 --- a/src/InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeParametersEqualityComparer.cs +++ b/src/InfiniFrame.NativeBridge/Managed/Parameters/Window/InfiniFrameNativeWindowParametersEqualityComparer.cs @@ -1,32 +1,32 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -namespace InfiniFrame.NativeBridge.Parameters; +namespace InfiniFrame.NativeBridge.Parameters.Window; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- /// -/// Compares two instances for value equality, +/// Compares two instances for value equality, /// ignoring callback handler fields. This comparer is intended for parameter-change /// detection where two instances with different callbacks but identical configuration /// values are considered equivalent. /// -internal sealed class InfiniFrameNativeParametersEqualityComparer : IEqualityComparer { +internal sealed class InfiniFrameNativeWindowParametersEqualityComparer : IEqualityComparer { /// /// Singleton instance of the equality comparer. /// - internal static readonly InfiniFrameNativeParametersEqualityComparer Instance = new(); + internal static readonly InfiniFrameNativeWindowParametersEqualityComparer Instance = new(); - private InfiniFrameNativeParametersEqualityComparer() {} + private InfiniFrameNativeWindowParametersEqualityComparer() {} /// - /// Determines whether two instances are equal +/// Determines whether two instances are equal /// by comparing all value fields. /// /// The first instance. /// The second instance. /// true if the instances are equal; otherwise, false. - public bool Equals(InfiniFrameNativeParameters x, InfiniFrameNativeParameters y) { + public bool Equals(InfiniFrameNativeWindowParameters x, InfiniFrameNativeWindowParameters y) { // Handlers are not checked because they are set by the constructor and are not user-configurable. // x.ClosingHandler == y.ClosingHandler // && x.ClosedHandler == y.ClosedHandler @@ -51,9 +51,6 @@ public bool Equals(InfiniFrameNativeParameters x, InfiniFrameNativeParameters y) if (x.TemporaryFilesPath != y.TemporaryFilesPath) return false; if (x.UserAgent != y.UserAgent) return false; if (x.BrowserControlInitParameters != y.BrowserControlInitParameters) return false; - if (x.WebView2RuntimePath != y.WebView2RuntimePath) return false; - if (x.NotificationRegistrationId != y.NotificationRegistrationId) return false; - if (x.WindowsAppUserModelId != y.WindowsAppUserModelId) return false; if (x.DefaultNotificationIcon != y.DefaultNotificationIcon) return false; // Runtime configuration @@ -126,12 +123,12 @@ public bool Equals(InfiniFrameNativeParameters x, InfiniFrameNativeParameters y) } /// - /// Returns a hash code for the specified instance +/// Returns a hash code for the specified instance /// based on its value fields. /// /// The instance to hash. /// A hash code value. - public int GetHashCode(InfiniFrameNativeParameters obj) { + public int GetHashCode(InfiniFrameNativeWindowParameters obj) { var hashCode = new HashCode(); // Content strings @@ -144,9 +141,6 @@ public int GetHashCode(InfiniFrameNativeParameters obj) { hashCode.Add(obj.TemporaryFilesPath); hashCode.Add(obj.UserAgent); hashCode.Add(obj.BrowserControlInitParameters); - hashCode.Add(obj.WebView2RuntimePath); - hashCode.Add(obj.NotificationRegistrationId); - hashCode.Add(obj.WindowsAppUserModelId); hashCode.Add(obj.DefaultNotificationIcon); // Runtime configuration diff --git a/src/InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeParametersMarshaller.cs b/src/InfiniFrame.NativeBridge/Managed/Parameters/Window/InfiniFrameNativeWindowParametersMarshaller.cs similarity index 93% rename from src/InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeParametersMarshaller.cs rename to src/InfiniFrame.NativeBridge/Managed/Parameters/Window/InfiniFrameNativeWindowParametersMarshaller.cs index 07bd05afe..4d86a6589 100644 --- a/src/InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeParametersMarshaller.cs +++ b/src/InfiniFrame.NativeBridge/Managed/Parameters/Window/InfiniFrameNativeWindowParametersMarshaller.cs @@ -6,20 +6,20 @@ using System.Runtime.InteropServices.Marshalling; using InfiniFrame.NativeBridge.Delegates; -namespace InfiniFrame.NativeBridge.Parameters; +namespace InfiniFrame.NativeBridge.Parameters.Window; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- /// -/// Custom marshaller for converting +/// Custom marshaller for converting /// to an unmanaged representation for native interop calls. /// [CustomMarshaller( - typeof(InfiniFrameNativeParameters), + typeof(InfiniFrameNativeWindowParameters), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedToUnmanagedIn) )] -internal static class InfiniFrameNativeParametersMarshaller { +internal static class InfiniFrameNativeWindowParametersMarshaller { // ----------------------------------------------------------------------------------------------------------------- // Methods @@ -71,8 +71,8 @@ private static IntPtr GetCustomSchemeName(IntPtr[]? values, int index) => values is not null && values.Length > index ? values[index] : IntPtr.Zero; /// - /// Unmanaged layout of used for native interop. - /// Field order must match the C++ InfiniFrameInitParams struct exactly. +/// Unmanaged layout of used for native interop. +/// Field order must match the C++ InfiniFrameWindowInitParams struct exactly. /// [StructLayout(LayoutKind.Sequential)] internal struct Unmanaged { @@ -86,9 +86,6 @@ internal struct Unmanaged { internal IntPtr TemporaryFilesPath; internal IntPtr UserAgent; internal IntPtr BrowserControlInitParameters; - internal IntPtr WebView2RuntimePath; - internal IntPtr NotificationRegistrationId; - internal IntPtr WindowsAppUserModelId; internal IntPtr DefaultNotificationIcon; // Runtime configuration @@ -96,6 +93,7 @@ internal struct Unmanaged { // Parent window internal IntPtr NativeParent; + internal IntPtr ApplicationInstance; // Event callbacks internal IntPtr ClosingHandler; @@ -186,7 +184,7 @@ internal struct Unmanaged { } /// - /// Marshals managed to the native layout. +/// Marshals managed to the native layout. /// [SuppressMessage("ReSharper", "NotAccessedField.Local")] internal ref struct ManagedToUnmanagedIn { @@ -212,7 +210,7 @@ internal ref struct ManagedToUnmanagedIn { /// Copies all values from the managed source into the unmanaged representation. /// /// The managed parameters source. - public void FromManaged(InfiniFrameNativeParameters managed) { + public void FromManaged(InfiniFrameNativeWindowParameters managed) { // Retain delegate references to prevent GC during native constructor call. _closingHandler = managed.ClosingHandler; _closedHandler = managed.ClosedHandler; @@ -240,9 +238,6 @@ public void FromManaged(InfiniFrameNativeParameters managed) { TemporaryFilesPath = ToUtf8Ptr(managed.TemporaryFilesPath), UserAgent = ToUtf8Ptr(managed.UserAgent), BrowserControlInitParameters = ToUtf8Ptr(managed.BrowserControlInitParameters), - WebView2RuntimePath = ToUtf8Ptr(managed.WebView2RuntimePath), - NotificationRegistrationId = ToUtf8Ptr(managed.NotificationRegistrationId), - WindowsAppUserModelId = ToUtf8Ptr(managed.WindowsAppUserModelId), DefaultNotificationIcon = ToUtf8Ptr(managed.DefaultNotificationIcon), // Runtime configuration @@ -250,6 +245,7 @@ public void FromManaged(InfiniFrameNativeParameters managed) { // Parent window NativeParent = managed.NativeParent, + ApplicationInstance = managed.ApplicationInstance, // Event callbacks ClosingHandler = ToFunctionPtr(managed.ClosingHandler), @@ -359,9 +355,6 @@ public void Free() { Marshal.FreeCoTaskMem(_unmanaged.TemporaryFilesPath); Marshal.FreeCoTaskMem(_unmanaged.UserAgent); Marshal.FreeCoTaskMem(_unmanaged.BrowserControlInitParameters); - Marshal.FreeCoTaskMem(_unmanaged.WebView2RuntimePath); - Marshal.FreeCoTaskMem(_unmanaged.NotificationRegistrationId); - Marshal.FreeCoTaskMem(_unmanaged.WindowsAppUserModelId); Marshal.FreeCoTaskMem(_unmanaged.DefaultNotificationIcon); Marshal.FreeCoTaskMem(_unmanaged.MenuBarJson); diff --git a/src/InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeParametersValidator.cs b/src/InfiniFrame.NativeBridge/Managed/Parameters/Window/InfiniFrameNativeWindowParametersValidator.cs similarity index 89% rename from src/InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeParametersValidator.cs rename to src/InfiniFrame.NativeBridge/Managed/Parameters/Window/InfiniFrameNativeWindowParametersValidator.cs index 5eeacce35..8efa85ae0 100644 --- a/src/InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeParametersValidator.cs +++ b/src/InfiniFrame.NativeBridge/Managed/Parameters/Window/InfiniFrameNativeWindowParametersValidator.cs @@ -4,26 +4,26 @@ using System.Runtime.InteropServices; using FluentValidation; -namespace InfiniFrame.NativeBridge.Parameters; +namespace InfiniFrame.NativeBridge.Parameters.Window; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- /// -/// Validates instances using FluentValidation rules. +/// Validates instances using FluentValidation rules. /// -public sealed class InfiniFrameNativeParametersValidator - : AbstractValidator { +public sealed class InfiniFrameNativeWindowParametersValidator + : AbstractValidator { // ----------------------------------------------------------------------------------------------------------------- // Constructors // ----------------------------------------------------------------------------------------------------------------- /// - /// Initializes a new instance of +/// Initializes a new instance of /// and configures all validation rules. /// - public InfiniFrameNativeParametersValidator() { + public InfiniFrameNativeWindowParametersValidator() { RuleFor(p => p.Size) - .Equal(Marshal.SizeOf()); + .Equal(Marshal.SizeOf()); RuleFor(p => p) .Must(p => @@ -87,12 +87,6 @@ public InfiniFrameNativeParametersValidator() { .NotNull().WithMessage("CustomSchemeNames must be specified.") .Must(names => names.Length <= 16).WithMessage("CustomSchemeNames must contain at most 16 names."); - RuleFor(p => p.WindowsAppUserModelId) - .NotEmpty() - .MaximumLength(128) - .Must(value => value is null || !value.Any(char.IsWhiteSpace)) - .When(p => p.WindowsAppUserModelId is not null) - .WithMessage("WindowsAppUserModelId must contain 1 to 128 characters and cannot contain whitespace."); } // ----------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame.Shared/Utilities/UnixPInvoke.cs b/src/InfiniFrame.NativeBridge/Managed/UnixPInvoke.cs similarity index 84% rename from src/InfiniFrame.Shared/Utilities/UnixPInvoke.cs rename to src/InfiniFrame.NativeBridge/Managed/UnixPInvoke.cs index cf991d63d..c438aebeb 100644 --- a/src/InfiniFrame.Shared/Utilities/UnixPInvoke.cs +++ b/src/InfiniFrame.NativeBridge/Managed/UnixPInvoke.cs @@ -1,9 +1,9 @@ -// --------------------------------------------------------------------------------------------------------------------- +// --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- using System.Runtime.InteropServices; -namespace InfiniFrame.Utilities; +namespace InfiniFrame.NativeBridge; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame.NativeBridge/Native/CMakeLists.txt b/src/InfiniFrame.NativeBridge/Native/CMakeLists.txt index 6ab5b1c82..99436664c 100644 --- a/src/InfiniFrame.NativeBridge/Native/CMakeLists.txt +++ b/src/InfiniFrame.NativeBridge/Native/CMakeLists.txt @@ -40,32 +40,34 @@ infiniframe_setup_dependencies() # ---------------------------------------------------------------------------------------------------------------------- set(COMMON_SOURCES src/Embedded/Embedded.cpp - src/Api/Utilities/ExportErrorState.cpp - src/Runtime/Shared/Window/WindowEvents.cpp - src/Runtime/Shared/Window/WindowState.cpp - src/Runtime/Shared/Operations/NativeOperation.cpp - src/Runtime/Shared/Operations/DialogOperation.cpp - src/Api/Exports/Exports.CustomSchemes.cpp - src/Api/Exports/Exports.Dispatch.cpp - src/Api/Exports/Exports.Dialog.cpp - src/Api/Exports/Exports.Events.cpp - src/Api/Exports/Exports.Lifecycle.cpp - src/Api/Exports/Exports.Memory.cpp - src/Api/Exports/Exports.Monitors.cpp - src/Api/Exports/Exports.Platform.Linux.cpp - src/Api/Exports/Exports.Platform.MacOs.cpp - src/Api/Exports/Exports.Platform.Windows.cpp - src/Api/Exports/Exports.Window.Actions.cpp - src/Api/Exports/Exports.Window.Getters.cpp - src/Api/Exports/Exports.Window.Navigation.cpp - src/Api/Exports/Exports.Window.Setters.cpp - src/Api/Exports/Exports.Window.Taskbar.cpp - src/Api/Exports/Exports.Menu.cpp + src/Runtime/Internal/Interop/Exports/ExportErrorState.cpp + src/Runtime/Internal/Application/InfiniFrameApplication.cpp + src/Runtime/Internal/Window/WindowEvents.cpp + src/Runtime/Internal/Window/WindowState.cpp + src/Runtime/Internal/Operations/NativeOperation.cpp + src/Runtime/Internal/Operations/DialogOperation.cpp + src/Abi/Exports.CustomSchemes.cpp + src/Abi/Exports.Dispatch.cpp + src/Abi/Exports.Dialog.cpp + src/Abi/Exports.Events.cpp + src/Abi/Exports.Lifecycle.cpp + src/Abi/Exports.Memory.cpp + src/Abi/Exports.Monitors.cpp + src/Abi/Exports.Platform.Linux.cpp + src/Abi/Exports.Platform.MacOs.cpp + src/Abi/Exports.Platform.Windows.cpp + src/Abi/Exports.Window.Actions.cpp + src/Abi/Exports.Window.Getters.cpp + src/Abi/Exports.Window.Navigation.cpp + src/Abi/Exports.Window.Setters.cpp + src/Abi/Exports.Window.Taskbar.cpp + src/Abi/Exports.Menu.cpp ) set(TEST_SOURCES - src/Api/Testing/Exports.Tests.cpp - src/Api/Testing/Exports.CustomSchemeResponseTests.cpp + src/Abi/Testing/Exports.Tests.cpp + src/Abi/Testing/Exports.CustomSchemeResponseTests.cpp + tests/PublicAbiCompileBoundary.cpp ) set(WINDOWS_SOURCES @@ -163,30 +165,31 @@ set(MAC_SOURCES set(HEADER_FILES src/Embedded/Embedded.h - src/Runtime/Shared/WebView/CustomSchemeResponse.h - src/Runtime/Shared/Operations/NativeOperation.h - src/Runtime/Shared/Operations/NavigationOperation.h - src/Api/Exports/Exports.h - src/Api/Utilities/ExportErrorState.h - src/Api/Utilities/ExportExecution.h - src/Api/Utilities/ExportStringHelpers.h - src/Api/Utilities/ExportValidation.h - src/Api/Utilities/Utilities.h - src/Runtime/Shared/Window/InfiniFrame.h - src/Runtime/Shared/Window/InfiniFrameDialog.h - src/Runtime/Shared/Window/InfiniFrameInitParams.h - src/Runtime/Shared/Window/InfiniFrameWindow.h - src/Runtime/Shared/Types/Basic.h - src/Runtime/Shared/Types/Callbacks.h - src/Runtime/Shared/Types/DialogButtons.h - src/Runtime/Shared/Types/DialogIcon.h - src/Runtime/Shared/Types/DialogResult.h - src/Runtime/Shared/Types/Monitor.h - src/Runtime/Shared/Utilities/Dimensions.h - src/Runtime/Shared/Utilities/ErrorCode.h - src/Runtime/Shared/Utilities/InteropStatus.h - src/Runtime/Shared/Utilities/StringArrayCopy.h - src/Runtime/Shared/Utilities/StringCopy.h + src/Runtime/Internal/WebView/CustomSchemeResponse.h + src/Runtime/Internal/Operations/NativeOperation.h + src/Runtime/Internal/Operations/NavigationOperation.h + src/Abi/Exports.h + src/Runtime/Internal/Interop/Exports/ExportErrorState.h + src/Runtime/Internal/Interop/Exports/ExportExecution.h + src/Runtime/Internal/Interop/Exports/ExportStringHelpers.h + src/Runtime/Internal/Interop/Exports/ExportValidation.h + src/Runtime/Internal/Interop/Types/InfiniFrameApplicationInitParams.h + src/Runtime/Internal/Interop/Types/InteropStatus.h + src/Runtime/Internal/Window/InfiniFrameDialog.h + src/Runtime/Internal/Interop/Types/InfiniFrameWindowInitParams.h + src/Runtime/Internal/Interop/Types/InfiniFrameWindow.h + src/Runtime/Internal/Application/InfiniFrameApplication.h + src/Runtime/Internal/Interop/Types/Basic.h + src/Runtime/Internal/Interop/Types/Callbacks.h + src/Runtime/Internal/Interop/Types/CustomSchemeResponse.h + src/Runtime/Internal/Interop/Types/DialogButtons.h + src/Runtime/Internal/Interop/Types/DialogIcon.h + src/Runtime/Internal/Interop/Types/DialogResult.h + src/Runtime/Internal/Interop/Types/Monitor.h + src/Runtime/Internal/Utilities/Dimensions.h + src/Runtime/Internal/Utilities/ErrorCode.h + src/Runtime/Internal/Utilities/StringArrayCopy.h + src/Runtime/Internal/Utilities/StringCopy.h ) if (WIN32) diff --git a/src/InfiniFrame.NativeBridge/Native/README.md b/src/InfiniFrame.NativeBridge/Native/README.md index 896725ce6..19356478a 100644 --- a/src/InfiniFrame.NativeBridge/Native/README.md +++ b/src/InfiniFrame.NativeBridge/Native/README.md @@ -10,8 +10,7 @@ Native/ BUILDING.md # Build performance tips src/ - Api/ - Exports/ # extern "C" functions (the public C ABI) + Abi/ # extern "C" functions (the public C ABI) Exports.Window.Actions.cpp # Center, Restore, Focus, Notifications Exports.Window.Getters.cpp # Query window state (size, position, flags) Exports.Window.Setters.cpp # Modify window state @@ -28,30 +27,15 @@ Native/ Exports.Platform.Windows.cpp # Windows-specific exports Exports.Platform.MacOs.cpp # macOS-specific exports Exports.Platform.Linux.cpp # Linux-specific exports - Testing/ # Test-only exports - Utilities/ # Export infrastructure (validation, error state, string helpers) - - Runtime/ - Shared/ # Cross-platform runtime code - Window/ # Window state, events, configuration - Operations/ # Async operation infrastructure - Platform/ # Platform detection + Runtime/Internal/ # Internal runtime, interop types, operations, and window state + Runtime/Platform/ # Windows, Linux, and macOS implementations Embedded/ # Embedded JS assets - - Platforms/ - Windows/ # WebView2 implementation - Linux/ # WebKitGTK implementation - MacOs/ # WKWebView implementation - - include/ - InfiniFrameWindow.h # Main public header InfiniFrameWindow class - Types/ # Shared ABI types (enums, structs) ``` ## Public C API -The public API is defined in `src/Api/Exports/` and consists of `extern "C"` functions with the prefix `InfiniFrameNative_`. These functions are called by the .NET managed layer via P/Invoke. +The public API is defined in `src/Abi/` and consists of `extern "C"` functions with the prefix `InfiniFrameNative_`. These functions are called by the .NET managed layer via P/Invoke. ### String Ownership diff --git a/src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.CustomSchemes.cpp b/src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.CustomSchemes.cpp similarity index 96% rename from src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.CustomSchemes.cpp rename to src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.CustomSchemes.cpp index d5c91a815..61099f026 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.CustomSchemes.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.CustomSchemes.cpp @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -#include "Api/Exports/Exports.h" +#include "Abi/Exports.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -19,4 +19,4 @@ EXPORTED InteropStatus InfiniFrameNative_AddCustomSchemeName(InfiniFrameWindow* window->AddCustomSchemeName(scheme); }); } -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Dialog.cpp b/src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Dialog.cpp similarity index 99% rename from src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Dialog.cpp rename to src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Dialog.cpp index 10cfa862c..20d072af9 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Dialog.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Dialog.cpp @@ -1,7 +1,8 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -#include "Api/Exports/Exports.h" +#include "Abi/Exports.h" +#include "Runtime/Internal/Window/InfiniFrameDialog.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -274,4 +275,4 @@ InfiniFrameNative_CancelDialog(InfiniFrameWindow* instance, const uint64_t opera *cancelled = window->CancelDialog(operationId); }); } -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Dispatch.cpp b/src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Dispatch.cpp similarity index 97% rename from src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Dispatch.cpp rename to src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Dispatch.cpp index cfcacfa2e..4cabbe803 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Dispatch.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Dispatch.cpp @@ -1,8 +1,8 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -#include "Api/Exports/Exports.h" -#include "Runtime/Shared/Operations/NativeOperation.h" +#include "Abi/Exports.h" +#include "Runtime/Internal/Operations/NativeOperation.h" #ifdef __linux__ #include "Runtime/Platform/Linux/Core/UiThread.Gtk.h" #endif @@ -87,4 +87,4 @@ EXPORTED InteropStatus InfiniFrameNative_CancelOperation( window->CancelOperation(operationId, static_cast(result)); }); } -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Events.cpp b/src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Events.cpp similarity index 99% rename from src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Events.cpp rename to src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Events.cpp index 02cdbd338..4d3088c88 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Events.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Events.cpp @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -#include "Api/Exports/Exports.h" +#include "Abi/Exports.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -103,4 +103,4 @@ EXPORTED InteropStatus InfiniFrameNative_SetDragDropEnabled(InfiniFrameWindow* i window->SetDragDropEnabled(enabled != 0); }); } -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Lifecycle.cpp b/src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Lifecycle.cpp similarity index 53% rename from src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Lifecycle.cpp rename to src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Lifecycle.cpp index 73b6c49e6..2d71bef1c 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Lifecycle.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Lifecycle.cpp @@ -1,7 +1,9 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -#include "Api/Exports/Exports.h" +#include "Abi/Exports.h" +#include "Runtime/Internal/Interop/Types/InfiniFrameWindowInitParams.h" +#include "Runtime/Internal/Application/InfiniFrameApplication.h" #ifdef __linux__ #include "Runtime/Platform/Linux/Core/UiThread.Gtk.h" #endif @@ -9,11 +11,89 @@ // Code // --------------------------------------------------------------------------------------------------------------------- extern "C" { +/// @brief Creates a native application instance. +EXPORTED InteropStatus InfiniFrameNativeApplication_ctor(InfiniFrameApplication** value) { + ResetOut(value, static_cast(nullptr)); + return RunExportStatus( + [&] { + if (!EnsureOutNotNull(value, "value")) return; + auto instance = std::make_unique(); + *value = instance.release(); + }); +} + +/// @brief Registers process-wide native application state. +EXPORTED InteropStatus InfiniFrameNativeApplication_Register(InfiniFrameApplication* instance) { + return RunExportStatus( + [&] { + if (!EnsureNotNull(instance, "instance")) return; + instance->Register(); + }); +} + +EXPORTED InteropStatus InfiniFrameNativeApplication_Configure( + InfiniFrameApplication* instance, + InfiniFrameApplicationInitParams* parameters +) { + return RunExportStatus( + [&] { + if (!EnsureNotNull(instance, "instance")) return; + if (!EnsureNotNull(parameters, "parameters")) return; + if (parameters->StructSize != static_cast(sizeof(InfiniFrameApplicationInitParams))) + throw std::invalid_argument("InfiniFrameApplicationInitParams.Size does not match native struct size."); + instance->Configure(*parameters); + }); +} + +/// @brief Runs the native application loop. +EXPORTED InteropStatus InfiniFrameNativeApplication_Run(InfiniFrameApplication* instance) { + return RunExportStatus( + [&] { + if (!EnsureNotNull(instance, "instance")) return; + instance->Run(); + }); +} + +/// @brief Requests native application shutdown. +EXPORTED InteropStatus InfiniFrameNativeApplication_Shutdown(InfiniFrameApplication* instance) { + return RunExportStatus( + [&] { + if (!EnsureNotNull(instance, "instance")) return; + instance->Shutdown(); + }); +} + +/// @brief Destroys a native application instance. +EXPORTED InteropStatus InfiniFrameNativeApplication_dtor(InfiniFrameApplication* instance) { + return RunExportStatus( + [&] { + if (!EnsureNotNull(instance, "instance")) return; +#ifdef _WIN32 + if (instance->GetWindowCount() != 0) + throw std::runtime_error("Cannot destroy a native application while windows are still alive."); +#endif + std::unique_ptr guard{instance}; + }); +} + +/// @brief Returns the number of native windows tracked by the application. +EXPORTED InteropStatus InfiniFrameNativeApplication_GetWindowCount( + InfiniFrameApplication* instance, + std::size_t* value +) { + ResetOut(value, static_cast(0)); + return RunExportStatus( + [&] { + if (!EnsureNotNull(instance, "instance") || !EnsureOutNotNull(value, "value")) return; + *value = instance->GetWindowCount(); + }); +} + /// @brief Creates a new native window with the given parameters. /// @param initParams Initialization parameters for the window. /// @param[out] value Receives the newly created window handle. /// @return InteropStatus -EXPORTED InteropStatus InfiniFrameNative_ctor(InfiniFrameInitParams* initParams, InfiniFrameWindow** value) { +EXPORTED InteropStatus InfiniFrameNative_ctor(InfiniFrameWindowInitParams* initParams, InfiniFrameWindow** value) { ResetOut(value, static_cast(nullptr)); return RunExportStatus( [&] { @@ -21,8 +101,8 @@ EXPORTED InteropStatus InfiniFrameNative_ctor(InfiniFrameInitParams* initParams, return; if (initParams == nullptr) throw std::invalid_argument("Argument 'initParams' is null."); - if (initParams->StructSize != static_cast(sizeof(InfiniFrameInitParams))) { - throw std::invalid_argument("InfiniFrameInitParams.Size does not match native struct size."); + if (initParams->StructSize != static_cast(sizeof(InfiniFrameWindowInitParams))) { + throw std::invalid_argument("InfiniFrameWindowInitParams.Size does not match native struct size."); } auto instance = std::make_unique(initParams); *value = instance.release(); @@ -86,6 +166,19 @@ EXPORTED InteropStatus InfiniFrameNative_SetReadyCallback( }); } +EXPORTED InteropStatus InfiniFrameNative_SetReadyFailureCallback( + InfiniFrameWindow* instance, + const ContextAction callback, + void* context + ) { + return RunWindowExportStatus( + instance, [&](InfiniFrameWindow* window) { + if (callback == nullptr) + throw std::invalid_argument("Argument 'callback' is null."); + window->SetReadyFailureCallback(callback, context); + }); +} + /// @brief Registers a callback for when teardown begins. /// @param instance The window handle. /// @param callback Context action invoked when teardown starts. @@ -114,4 +207,4 @@ EXPORTED InteropStatus InfiniFrameNative_Shutdown() { }); } #endif -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Memory.cpp b/src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Memory.cpp similarity index 98% rename from src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Memory.cpp rename to src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Memory.cpp index 273f8e961..6c4bcf844 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Memory.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Memory.cpp @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -#include "Api/Exports/Exports.h" +#include "Abi/Exports.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -51,4 +51,4 @@ EXPORTED InteropStatus InfiniFrameNative_GetLastErrorMessage(const char** value) *value = GetLastErrorMessageCopy(); return InteropStatus::Success; } -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Menu.cpp b/src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Menu.cpp similarity index 98% rename from src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Menu.cpp rename to src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Menu.cpp index 9bfcc3d62..8cdf5886b 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Menu.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Menu.cpp @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -#include "Api/Exports/Exports.h" +#include "Abi/Exports.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -57,4 +57,4 @@ EXPORTED InteropStatus InfiniFrameNative_ClickMenuItem(InfiniFrameWindow* instan window->ClickMenuItemById(menuItemId); }); } -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Monitors.cpp b/src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Monitors.cpp similarity index 97% rename from src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Monitors.cpp rename to src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Monitors.cpp index 11a4c2525..bf3579900 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Monitors.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Monitors.cpp @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -#include "Api/Exports/Exports.h" +#include "Abi/Exports.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -20,4 +20,4 @@ EXPORTED InteropStatus InfiniFrameNative_GetAllMonitors( window->GetAllMonitors(callback); }); } -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Platform.Linux.cpp b/src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Platform.Linux.cpp similarity index 96% rename from src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Platform.Linux.cpp rename to src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Platform.Linux.cpp index 5e36b5b19..fe4c74b75 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Platform.Linux.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Platform.Linux.cpp @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -#include "Api/Exports/Exports.h" +#include "Abi/Exports.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -21,4 +21,4 @@ EXPORTED InteropStatus InfiniFrameNative_getGtkWindow_linux(InfiniFrameWindow* i }); } #endif -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Platform.MacOs.cpp b/src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Platform.MacOs.cpp similarity index 97% rename from src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Platform.MacOs.cpp rename to src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Platform.MacOs.cpp index 885b695fe..5b392ba02 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Platform.MacOs.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Platform.MacOs.cpp @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -#include "Api/Exports/Exports.h" +#include "Abi/Exports.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -31,4 +31,4 @@ EXPORTED InteropStatus InfiniFrameNative_getNSWindow_mac(InfiniFrameWindow* inst }); } #endif -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Platform.Windows.cpp b/src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Platform.Windows.cpp similarity index 99% rename from src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Platform.Windows.cpp rename to src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Platform.Windows.cpp index b0dfc3b6c..182861895 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Platform.Windows.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Platform.Windows.cpp @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -#include "Api/Exports/Exports.h" +#include "Abi/Exports.h" #ifdef _WIN32 #include "Runtime/Platform/Windows/Window.Win32.Context.h" #endif @@ -85,4 +85,4 @@ EXPORTED InteropStatus InfiniFrameNative_getWebView2RuntimeVersion_win32(const c }); } #endif -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Window.Actions.cpp b/src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Window.Actions.cpp similarity index 99% rename from src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Window.Actions.cpp rename to src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Window.Actions.cpp index 379dd47f0..d791cb8ff 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Window.Actions.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Window.Actions.cpp @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -#include "Api/Exports/Exports.h" +#include "Abi/Exports.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -133,4 +133,4 @@ EXPORTED InteropStatus InfiniFrameNative_CancelNotification( window->CancelNotification(operationId, canceled); }); } -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Window.Getters.cpp b/src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Window.Getters.cpp similarity index 99% rename from src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Window.Getters.cpp rename to src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Window.Getters.cpp index 395f695e1..e1d426806 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Window.Getters.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Window.Getters.cpp @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -#include "Api/Exports/Exports.h" +#include "Abi/Exports.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -445,4 +445,4 @@ EXPORTED InteropStatus InfiniFrameNative_GetIconFileName(InfiniFrameWindow* inst *value = window->GetIconFileName(); }); } -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Window.Navigation.cpp b/src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Window.Navigation.cpp similarity index 99% rename from src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Window.Navigation.cpp rename to src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Window.Navigation.cpp index 80c5765a9..9cb23788c 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Window.Navigation.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Window.Navigation.cpp @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -#include "Api/Exports/Exports.h" +#include "Abi/Exports.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -113,4 +113,4 @@ EXPORTED InteropStatus InfiniFrameNative_GetCurrentUrl(InfiniFrameWindow* instan *value = window->GetCurrentUrl(); }); } -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Window.Setters.cpp b/src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Window.Setters.cpp similarity index 99% rename from src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Window.Setters.cpp rename to src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Window.Setters.cpp index 7dde8b3d7..569f5ce55 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Window.Setters.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Window.Setters.cpp @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -#include "Api/Exports/Exports.h" +#include "Abi/Exports.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -248,4 +248,4 @@ EXPORTED InteropStatus InfiniFrameNative_SetZoom(InfiniFrameWindow* instance, co window->SetZoom(zoom); }); } -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Window.Taskbar.cpp b/src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Window.Taskbar.cpp similarity index 98% rename from src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Window.Taskbar.cpp rename to src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Window.Taskbar.cpp index c01f6ce6e..3ebc50bf2 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.Window.Taskbar.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.Window.Taskbar.cpp @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -#include "Api/Exports/Exports.h" +#include "Abi/Exports.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -76,4 +76,4 @@ EXPORTED InteropStatus InfiniFrameNative_GetTaskbarProgressSupported( window->GetTaskbarProgressSupported(supported); }); } -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.h b/src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.h similarity index 64% rename from src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.h rename to src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.h index f274d916b..3b5ae0a3e 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Api/Exports/Exports.h +++ b/src/InfiniFrame.NativeBridge/Native/src/Abi/Exports.h @@ -14,9 +14,24 @@ #define EXPORTED #endif -#include "Runtime/Shared/Types/Basic.h" -#include "Runtime/Shared/Utilities/InteropStatus.h" -#include "Api/Utilities/Utilities.h" +#include "Runtime/Internal/Interop/Types/Basic.h" +#include "Runtime/Internal/Interop/Types/InteropStatus.h" +#include "Runtime/Internal/Interop/Exports/ExportErrorState.h" +#include "Runtime/Internal/Interop/Exports/ExportExecution.h" +#include "Runtime/Internal/Interop/Exports/ExportStringHelpers.h" +#include "Runtime/Internal/Interop/Exports/ExportValidation.h" + +using infiniframe::exports::EnsureNotNull; +using infiniframe::exports::EnsureOutNotNull; +using infiniframe::exports::GetLastErrorMessageCopy; +using infiniframe::exports::ResetOut; +using infiniframe::exports::ResetOut2; +using infiniframe::exports::RunExportStatus; +using infiniframe::exports::RunReturnExport; +using infiniframe::exports::RunWindowExportStatus; +using infiniframe::exports::RunWindowReturnExport; +using infiniframe::exports::NullToEmpty; +using infiniframe::exports::DuplicateString; // --------------------------------------------------------------------------------------------------------------------- // String Ownership Contract @@ -36,4 +51,4 @@ // // NULL semantics: // Returning nullptr from an owned-string function means "no value" (e.g. no -// file selected). The caller must still check before calling FreeString. \ No newline at end of file +// file selected). The caller must still check before calling FreeString. diff --git a/src/InfiniFrame.NativeBridge/Native/src/Api/Testing/Exports.CustomSchemeResponseTests.cpp b/src/InfiniFrame.NativeBridge/Native/src/Abi/Testing/Exports.CustomSchemeResponseTests.cpp similarity index 96% rename from src/InfiniFrame.NativeBridge/Native/src/Api/Testing/Exports.CustomSchemeResponseTests.cpp rename to src/InfiniFrame.NativeBridge/Native/src/Abi/Testing/Exports.CustomSchemeResponseTests.cpp index 1bee06f24..9490b4084 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Api/Testing/Exports.CustomSchemeResponseTests.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Abi/Testing/Exports.CustomSchemeResponseTests.cpp @@ -1,9 +1,9 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -#include "Runtime/Shared/Window/InfiniFrame.h" -#include "Api/Exports/Exports.h" -#include "Runtime/Shared/WebView/CustomSchemeResponse.h" +#include "Abi/Exports.h" +#include "Runtime/Internal/WebView/CustomSchemeResponse.h" +#include "Runtime/Internal/Utilities/StringCopy.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -116,4 +116,4 @@ EXPORTED InteropStatus InfiniFrameNativeTests_BuildHeaders( } } -#endif \ No newline at end of file +#endif diff --git a/src/InfiniFrame.NativeBridge/Native/src/Api/Testing/Exports.Tests.cpp b/src/InfiniFrame.NativeBridge/Native/src/Abi/Testing/Exports.Tests.cpp similarity index 83% rename from src/InfiniFrame.NativeBridge/Native/src/Api/Testing/Exports.Tests.cpp rename to src/InfiniFrame.NativeBridge/Native/src/Abi/Testing/Exports.Tests.cpp index 064d30c9f..0d33185cd 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Api/Testing/Exports.Tests.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Abi/Testing/Exports.Tests.cpp @@ -1,9 +1,10 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -#include "Runtime/Shared/Window/InfiniFrame.h" -#include "Api/Exports/Exports.h" -#include "Runtime/Shared/WebView/CustomSchemeResponse.h" +#include "Runtime/Internal/Interop/Types/InfiniFrameWindowInitParams.h" +#include "Runtime/Internal/Interop/Types/InfiniFrameApplicationInitParams.h" +#include "Abi/Exports.h" +#include "Runtime/Internal/WebView/CustomSchemeResponse.h" #ifdef _WIN32 #include "Runtime/Platform/Windows/DarkMode.h" #endif @@ -28,8 +29,8 @@ EXPORTED InteropStatus InfiniFrameNativeTests_MacPooledHostCount(size_t* value) } #endif EXPORTED InteropStatus InfiniFrameNativeTests_NativeParametersReturnAsIs( - const InfiniFrameInitParams* params, - InfiniFrameInitParams** new_params + const InfiniFrameWindowInitParams* params, + InfiniFrameWindowInitParams** new_params ) { if (new_params != nullptr) { *new_params = nullptr; @@ -42,7 +43,7 @@ EXPORTED InteropStatus InfiniFrameNativeTests_NativeParametersReturnAsIs( return; } - *new_params = new InfiniFrameInitParams(); + *new_params = new InfiniFrameWindowInitParams(); // Content strings (*new_params)->StartString = DuplicateString(params->StartString); @@ -54,9 +55,6 @@ EXPORTED InteropStatus InfiniFrameNativeTests_NativeParametersReturnAsIs( (*new_params)->TemporaryFilesPath = DuplicateString(params->TemporaryFilesPath); (*new_params)->UserAgent = DuplicateString(params->UserAgent); (*new_params)->BrowserControlInitParameters = DuplicateString(params->BrowserControlInitParameters); - (*new_params)->WebView2RuntimePath = DuplicateString(params->WebView2RuntimePath); - (*new_params)->NotificationRegistrationId = DuplicateString(params->NotificationRegistrationId); - (*new_params)->WindowsAppUserModelId = DuplicateString(params->WindowsAppUserModelId); (*new_params)->DefaultNotificationIcon = DuplicateString(params->DefaultNotificationIcon); // Runtime configuration @@ -64,6 +62,7 @@ EXPORTED InteropStatus InfiniFrameNativeTests_NativeParametersReturnAsIs( // Parent window (*new_params)->ParentInstance = params->ParentInstance; + (*new_params)->ApplicationInstance = params->ApplicationInstance; // Event callbacks (*new_params)->ClosingHandler = params->ClosingHandler; @@ -79,7 +78,7 @@ EXPORTED InteropStatus InfiniFrameNativeTests_NativeParametersReturnAsIs( (*new_params)->DebugEventHandler = params->DebugEventHandler; // Custom scheme support - for (size_t i = 0; i < InfiniFrameInitParams::MaxCustomSchemeNames; ++i) { + for (size_t i = 0; i < InfiniFrameWindowInitParams::MaxCustomSchemeNames; ++i) { (*new_params)->CustomSchemeNames[i] = params->CustomSchemeNames[i] != nullptr ? DuplicateString(params->CustomSchemeNames[i]) : nullptr; @@ -143,7 +142,7 @@ EXPORTED InteropStatus InfiniFrameNativeTests_NativeParametersReturnAsIs( }); } -EXPORTED InteropStatus InfiniFrameNativeTests_FreeInitParams(InfiniFrameInitParams* params) { +EXPORTED InteropStatus InfiniFrameNativeTests_FreeInitParams(InfiniFrameWindowInitParams* params) { return RunExportStatus( [&] { if (!EnsureNotNull(params, "params")) { @@ -158,11 +157,8 @@ EXPORTED InteropStatus InfiniFrameNativeTests_FreeInitParams(InfiniFrameInitPara delete[] params->TemporaryFilesPath; delete[] params->UserAgent; delete[] params->BrowserControlInitParameters; - delete[] params->WebView2RuntimePath; - delete[] params->NotificationRegistrationId; - delete[] params->WindowsAppUserModelId; delete[] params->DefaultNotificationIcon; - for (size_t i = 0; i < InfiniFrameInitParams::MaxCustomSchemeNames; ++i) { + for (size_t i = 0; i < InfiniFrameWindowInitParams::MaxCustomSchemeNames; ++i) { delete[] params->CustomSchemeNames[i]; } delete[] params->MenuBarJson; @@ -171,6 +167,41 @@ EXPORTED InteropStatus InfiniFrameNativeTests_FreeInitParams(InfiniFrameInitPara }); } +EXPORTED InteropStatus InfiniFrameNativeTests_NativeApplicationParametersReturnAsIs( + const InfiniFrameApplicationInitParams* params, + InfiniFrameApplicationInitParams** new_params + ) { + if (new_params != nullptr) *new_params = nullptr; + + return RunExportStatus( + [&] { + if (!EnsureNotNull(params, "params") || + !EnsureNotNull(new_params, "new_params", ::InteropStatus::OutParameterSetToInvalidNull)) { + return; + } + + *new_params = new InfiniFrameApplicationInitParams { + DuplicateString(params->WebView2RuntimePath), + DuplicateString(params->NotificationRegistrationId), + DuplicateString(params->AppUserModelId), + DuplicateString(params->DefaultNotificationIcon), + params->StructSize + }; + }); +} + +EXPORTED InteropStatus InfiniFrameNativeTests_FreeApplicationParameters(InfiniFrameApplicationInitParams* params) { + return RunExportStatus( + [&] { + if (!EnsureNotNull(params, "params")) return; + delete[] params->WebView2RuntimePath; + delete[] params->NotificationRegistrationId; + delete[] params->AppUserModelId; + delete[] params->DefaultNotificationIcon; + delete params; + }); +} + EXPORTED InteropStatus InfiniFrameNativeTests_ConsumeCustomSchemeResponse( void* callbackPointer, uint64_t* contentLength, @@ -228,4 +259,4 @@ EXPORTED InteropStatus InfiniFrameNativeTests_IsColorSchemeChange(const LPARAM l #endif } -#endif \ No newline at end of file +#endif diff --git a/src/InfiniFrame.NativeBridge/Native/src/Api/Utilities/Utilities.h b/src/InfiniFrame.NativeBridge/Native/src/Api/Utilities/Utilities.h deleted file mode 100644 index 5e5c92069..000000000 --- a/src/InfiniFrame.NativeBridge/Native/src/Api/Utilities/Utilities.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once -// --------------------------------------------------------------------------------------------------------------------- -// Imports -// --------------------------------------------------------------------------------------------------------------------- -#include "ExportErrorState.h" -#include "ExportExecution.h" -#include "ExportStringHelpers.h" -#include "ExportValidation.h" - -// --------------------------------------------------------------------------------------------------------------------- -// Code -// --------------------------------------------------------------------------------------------------------------------- -using infiniframe::exports::EnsureNotNull; -using infiniframe::exports::EnsureOutNotNull; -using infiniframe::exports::GetLastErrorMessageCopy; -using infiniframe::exports::ResetOut; -using infiniframe::exports::ResetOut2; -using infiniframe::exports::RunExportStatus; -using infiniframe::exports::RunReturnExport; -using infiniframe::exports::RunWindowExportStatus; -using infiniframe::exports::RunWindowReturnExport; -using infiniframe::exports::NullToEmpty; -using infiniframe::exports::DuplicateString; \ No newline at end of file diff --git a/src/InfiniFrame.NativeBridge/Native/src/Dependencies/VENDORING.md b/src/InfiniFrame.NativeBridge/Native/src/Dependencies/VENDORING.md index a1fddb6bb..9021e88e8 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Dependencies/VENDORING.md +++ b/src/InfiniFrame.NativeBridge/Native/src/Dependencies/VENDORING.md @@ -1,6 +1,6 @@ # Vendored Native Dependencies -`src/InfiniFrame.Native/Dependencies` contains vendored native dependency artifacts used by `InfiniFrame.Native`. +`src/InfiniFrame.NativeBridge/Native/src/Dependencies` contains vendored native dependency artifacts used by `InfiniFrame.Native`. ## Libraries diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Application/InfiniFrameApplication.cpp b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Application/InfiniFrameApplication.cpp new file mode 100644 index 000000000..c838ea0d0 --- /dev/null +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Application/InfiniFrameApplication.cpp @@ -0,0 +1,243 @@ +// --------------------------------------------------------------------------------------------------------------------- +// Imports +// --------------------------------------------------------------------------------------------------------------------- +#include "Runtime/Internal/Application/InfiniFrameApplication.h" +#include +#include +#include +#include +#include +#ifdef _WIN32 +#include +#include +#include "Runtime/Platform/Windows/Window.Win32.Context.h" +#include "Dependencies/wintoastlib/wintoastlib.h" +#endif + +// --------------------------------------------------------------------------------------------------------------------- +// Code +// --------------------------------------------------------------------------------------------------------------------- +struct InfiniFrameApplicationImpl { + mutable std::mutex mutex; + std::unordered_set windows; + std::unordered_set liveWindows; + bool registered = false; + bool shutdownRequested = false; + std::string webView2RuntimePath; + std::string notificationRegistrationId; + std::string appUserModelId; + std::string defaultNotificationIcon; + bool notificationsRegistered = false; +#ifdef _WIN32 + unsigned long runThreadId = 0; + bool running = false; +#endif +}; + +namespace { + std::atomic applicationInstance = nullptr; +} + +#ifdef _WIN32 +namespace { + std::wstring ToWindowsString(const char* value) { + if (value == nullptr || value[0] == '\0') return {}; + const int length = MultiByteToWideChar(CP_UTF8, 0, value, -1, nullptr, 0); + if (length <= 1) return {}; + std::wstring result(static_cast(length), L'\0'); + MultiByteToWideChar(CP_UTF8, 0, value, -1, result.data(), length); + result.resize(static_cast(length - 1)); + return result; + } +} +#endif + +InfiniFrameApplication::InfiniFrameApplication() + : _impl(std::make_unique()) { + InfiniFrameApplication* expected = nullptr; + if (!applicationInstance.compare_exchange_strong(expected, this, std::memory_order_acq_rel)) + throw std::runtime_error("Only one InfiniFrameApplication may exist per process."); +} + +InfiniFrameApplication::~InfiniFrameApplication() { + std::lock_guard lock(_impl->mutex); + _impl->windows.clear(); + _impl->liveWindows.clear(); + InfiniFrameApplication* expected = this; + applicationInstance.compare_exchange_strong(expected, nullptr, std::memory_order_acq_rel); +} + +InfiniFrameApplication* InfiniFrameApplication::GetInstance() noexcept { + return applicationInstance.load(std::memory_order_acquire); +} + +void InfiniFrameApplication::Register() { + std::lock_guard lock(_impl->mutex); +#ifdef _WIN32 + InfiniFrameWindow::Register(GetModuleHandle(nullptr)); + if (_impl->registered) return; + if (!_impl->appUserModelId.empty()) { + const std::wstring appUserModelId = ToWindowsString(_impl->appUserModelId.c_str()); + const HRESULT result = SetCurrentProcessExplicitAppUserModelID(appUserModelId.c_str()); + if (FAILED(result)) + throw std::runtime_error("Could not set the application Windows AppUserModelID."); + } + +#endif + _impl->registered = true; +} + +void InfiniFrameApplication::Configure(const InfiniFrameApplicationInitParams& parameters) { + std::lock_guard lock(_impl->mutex); + _impl->webView2RuntimePath = parameters.WebView2RuntimePath == nullptr ? "" : parameters.WebView2RuntimePath; + _impl->notificationRegistrationId = parameters.NotificationRegistrationId == nullptr ? "" : parameters.NotificationRegistrationId; + _impl->appUserModelId = parameters.AppUserModelId == nullptr ? "" : parameters.AppUserModelId; + _impl->defaultNotificationIcon = parameters.DefaultNotificationIcon == nullptr ? "" : parameters.DefaultNotificationIcon; +} + +#ifdef _WIN32 +void InfiniFrameApplication::EnsureNotificationsInitialized(const char* appName) { + std::lock_guard lock(_impl->mutex); + if (_impl->notificationsRegistered) return; + const std::string identity = !_impl->notificationRegistrationId.empty() + ? _impl->notificationRegistrationId + : !_impl->appUserModelId.empty() ? _impl->appUserModelId + : appName != nullptr && appName[0] != '\0' ? appName + : "InfiniFrame"; + + const char* effectiveAppName = appName != nullptr && appName[0] != '\0' ? appName : identity.c_str(); + const std::wstring windowsAppName = ToWindowsString(effectiveAppName); + const std::wstring windowsIdentity = ToWindowsString(identity.c_str()); + WinToastLib::setDebugOutputEnabled(false); + WinToastLib::WinToast::instance()->setAppName(windowsAppName); + WinToastLib::WinToast::instance()->setAppUserModelId(windowsIdentity); + if (!WinToastLib::WinToast::instance()->initialize()) + throw std::runtime_error("Could not initialize application notifications."); + _impl->notificationsRegistered = true; +} +#endif + +void InfiniFrameApplication::Run() noexcept { +#ifdef _WIN32 + MSG message = {}; + PeekMessage(&message, nullptr, WM_USER, WM_USER, PM_NOREMOVE); + + { + std::lock_guard lock(_impl->mutex); + _impl->runThreadId = GetCurrentThreadId(); + _impl->running = true; + // Shutdown is a drain request, not permission to abandon HWNDs. A close can + // be deferred while WebView2 is creating its controller, so the message pump + // must continue until every tracked window has reached WM_DESTROY. + if (_impl->windows.empty()) { + _impl->running = false; + return; + } + } + + while (true) { + { + std::lock_guard lock(_impl->mutex); + if (_impl->shutdownRequested && _impl->windows.empty()) break; + } + + MsgWaitForMultipleObjectsEx(0, nullptr, 50, QS_ALLINPUT, MWMO_INPUTAVAILABLE); + while (PeekMessage(&message, nullptr, 0, 0, PM_REMOVE)) { + if (message.message == WM_QUIT) { + std::vector windows; + { + std::lock_guard lock(_impl->mutex); + _impl->shutdownRequested = true; + windows.assign(_impl->windows.begin(), _impl->windows.end()); + } + // WM_QUIT does not dispatch to HWNDs. Post WM_CLOSE so normal + // teardown drains every tracked window before returning. + for (InfiniFrameWindow* window : windows) + window->Close(); + break; + } + TranslateMessage(&message); + DispatchMessage(&message); + } + } + + std::lock_guard lock(_impl->mutex); + _impl->running = false; + _impl->runThreadId = 0; +#else + // Other platforms retain their existing loop until their application + // lifecycle integrations are implemented. +#endif +} + +void InfiniFrameApplication::Shutdown() noexcept { + std::lock_guard lock(_impl->mutex); + _impl->shutdownRequested = true; +#ifdef _WIN32 + if (_impl->runThreadId != 0) { + PostThreadMessage(_impl->runThreadId, WM_QUIT, 0, 0); + } +#endif +} + +void InfiniFrameApplication::TrackWindow(InfiniFrameWindow* window) { + if (window == nullptr) return; + std::lock_guard lock(_impl->mutex); + _impl->windows.insert(window); + _impl->liveWindows.insert(window); +} + +void InfiniFrameApplication::UntrackWindow(InfiniFrameWindow* window) noexcept { + if (window == nullptr) return; + std::lock_guard lock(_impl->mutex); + _impl->windows.erase(window); + _impl->liveWindows.erase(window); +} + +void InfiniFrameApplication::NotifyWindowClosed(InfiniFrameWindow* window) noexcept { + if (window == nullptr) return; + + std::lock_guard lock(_impl->mutex); + _impl->windows.erase(window); +#ifdef _WIN32 + if (_impl->running && _impl->windows.empty()) + PostThreadMessage(_impl->runThreadId, WM_QUIT, 0, 0); +#endif +} + +std::size_t InfiniFrameApplication::GetWindowCount() const noexcept { + std::lock_guard lock(_impl->mutex); + return _impl->liveWindows.size(); +} + +const char* InfiniFrameApplication::GetWebView2RuntimePath() const noexcept { + thread_local std::string value; + std::lock_guard lock(_impl->mutex); + value = _impl->webView2RuntimePath; + return value.c_str(); +} + +const char* InfiniFrameApplication::GetNotificationRegistrationId() const noexcept { + thread_local std::string value; + std::lock_guard lock(_impl->mutex); + value = _impl->notificationRegistrationId; + return value.c_str(); +} + +const char* InfiniFrameApplication::GetAppUserModelId() const noexcept { + thread_local std::string value; + std::lock_guard lock(_impl->mutex); + value = _impl->appUserModelId; + return value.c_str(); +} + +const char* InfiniFrameApplication::GetDefaultNotificationIcon() const noexcept { + thread_local std::string value; + std::lock_guard lock(_impl->mutex); + value = _impl->defaultNotificationIcon; + return value.c_str(); +} + +bool InfiniFrameApplication::HasNotificationRegistration() const noexcept { + return _impl->notificationsRegistered; +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Application/InfiniFrameApplication.h b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Application/InfiniFrameApplication.h new file mode 100644 index 000000000..0f9e48c24 --- /dev/null +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Application/InfiniFrameApplication.h @@ -0,0 +1,47 @@ +#pragma once +// --------------------------------------------------------------------------------------------------------------------- +// Imports +// --------------------------------------------------------------------------------------------------------------------- +#include +#include + +#include "Runtime/Internal/Interop/Types/InfiniFrameApplicationInitParams.h" + +class InfiniFrameWindow; +struct InfiniFrameApplicationImpl; +// --------------------------------------------------------------------------------------------------------------------- +// Code +// --------------------------------------------------------------------------------------------------------------------- +/** + * @brief Process-scoped owner and registry for native InfiniFrame windows. + * + * The registry is independent of the platform message loop so the existing + * window ABI can remain compatible while application-loop ownership is added. + */ +class InfiniFrameApplication { + public: + InfiniFrameApplication(); + ~InfiniFrameApplication(); + + InfiniFrameApplication(const InfiniFrameApplication&) = delete; + InfiniFrameApplication& operator=(const InfiniFrameApplication&) = delete; + + [[nodiscard]] static InfiniFrameApplication* GetInstance() noexcept; + void Register(); + void Configure(const InfiniFrameApplicationInitParams& parameters); + void Run() noexcept; + void Shutdown() noexcept; + void TrackWindow(InfiniFrameWindow* window); + void UntrackWindow(InfiniFrameWindow* window) noexcept; + void NotifyWindowClosed(InfiniFrameWindow* window) noexcept; + [[nodiscard]] std::size_t GetWindowCount() const noexcept; + [[nodiscard]] const char* GetWebView2RuntimePath() const noexcept; + [[nodiscard]] const char* GetNotificationRegistrationId() const noexcept; + [[nodiscard]] const char* GetAppUserModelId() const noexcept; + [[nodiscard]] const char* GetDefaultNotificationIcon() const noexcept; + [[nodiscard]] bool HasNotificationRegistration() const noexcept; + void EnsureNotificationsInitialized(const char* appName); + + private: + std::unique_ptr _impl; +}; diff --git a/src/InfiniFrame.NativeBridge/Native/src/Api/Utilities/ExportErrorState.cpp b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Exports/ExportErrorState.cpp similarity index 99% rename from src/InfiniFrame.NativeBridge/Native/src/Api/Utilities/ExportErrorState.cpp rename to src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Exports/ExportErrorState.cpp index 3fc39a1f6..729f2d201 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Api/Utilities/ExportErrorState.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Exports/ExportErrorState.cpp @@ -8,4 +8,4 @@ namespace infiniframe::exports { thread_local std::string g_lastErrorMessage; thread_local InteropStatus g_lastStatus = InteropStatus::Success; -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Api/Utilities/ExportErrorState.h b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Exports/ExportErrorState.h similarity index 95% rename from src/InfiniFrame.NativeBridge/Native/src/Api/Utilities/ExportErrorState.h rename to src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Exports/ExportErrorState.h index cf28afc77..fd1e6568e 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Api/Utilities/ExportErrorState.h +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Exports/ExportErrorState.h @@ -16,9 +16,8 @@ #include #include -#include "Runtime/Shared/Window/InfiniFrame.h" -#include "Runtime/Shared/Utilities/InteropStatus.h" -#include "Api/Utilities/ExportStringHelpers.h" +#include "Runtime/Internal/Interop/Types/InteropStatus.h" +#include "Runtime/Internal/Interop/Exports/ExportStringHelpers.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame.NativeBridge/Native/src/Api/Utilities/ExportExecution.h b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Exports/ExportExecution.h similarity index 97% rename from src/InfiniFrame.NativeBridge/Native/src/Api/Utilities/ExportExecution.h rename to src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Exports/ExportExecution.h index 48a985951..c3cf4a8dc 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Api/Utilities/ExportExecution.h +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Exports/ExportExecution.h @@ -5,8 +5,8 @@ #include #include -#include "Runtime/Shared/Window/InfiniFrame.h" -#include "Runtime/Shared/Utilities/InteropStatus.h" +#include "Runtime/Internal/Interop/Types/InfiniFrameWindow.h" +#include "Runtime/Internal/Interop/Types/InteropStatus.h" #include "ExportErrorState.h" #include "ExportValidation.h" // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame.NativeBridge/Native/src/Api/Utilities/ExportStringHelpers.h b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Exports/ExportStringHelpers.h similarity index 97% rename from src/InfiniFrame.NativeBridge/Native/src/Api/Utilities/ExportStringHelpers.h rename to src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Exports/ExportStringHelpers.h index bb6d7fd25..8e1a5080b 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Api/Utilities/ExportStringHelpers.h +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Exports/ExportStringHelpers.h @@ -13,7 +13,7 @@ #include #include -#include "Runtime/Shared/Window/InfiniFrame.h" +#include "Runtime/Internal/Utilities/StringCopy.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame.NativeBridge/Native/src/Api/Utilities/ExportValidation.h b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Exports/ExportValidation.h similarity index 96% rename from src/InfiniFrame.NativeBridge/Native/src/Api/Utilities/ExportValidation.h rename to src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Exports/ExportValidation.h index fcc949f38..29c3ccce1 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Api/Utilities/ExportValidation.h +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Exports/ExportValidation.h @@ -16,8 +16,7 @@ #include #include -#include "Runtime/Shared/Window/InfiniFrame.h" -#include "Runtime/Shared/Utilities/InteropStatus.h" +#include "Runtime/Internal/Interop/Types/InteropStatus.h" #include "ExportErrorState.h" // --------------------------------------------------------------------------------------------------------------------- // Code diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Types/Basic.h b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Types/Basic.h similarity index 100% rename from src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Types/Basic.h rename to src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Types/Basic.h diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Types/Callbacks.h b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Types/Callbacks.h similarity index 79% rename from src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Types/Callbacks.h rename to src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Types/Callbacks.h index 1fd608230..38e0edf3e 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Types/Callbacks.h +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Types/Callbacks.h @@ -4,8 +4,9 @@ // --------------------------------------------------------------------------------------------------------------------- #include -#include "Basic.h" -#include "Monitor.h" +#include "Runtime/Internal/Interop/Types/Basic.h" +#include "Runtime/Internal/Interop/Types/Monitor.h" +#include "Runtime/Internal/Interop/Types/CustomSchemeResponse.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -63,40 +64,6 @@ using DebugEventCallback = void (*)( const char* platformPayload ); -/** Version 1 custom-scheme response body kinds. Kind 2 is reserved for a future pull-based stream ABI. */ -enum class CustomSchemeBodyKind : uint32_t { - Buffered = 1, - Stream = 2 -}; - -using ReleaseCustomSchemeResponseCallback = void (*)(void* ownerContext); - -/** - * @brief Versioned custom-scheme response descriptor shared with .NET. - * - * The native caller owns this descriptor. The producer owns Body, ContentTypeUtf8, and OwnerContext until native calls - * Release(OwnerContext) exactly once. Native must not free any field directly. ReservedRead/ReservedSeek are ABI space - * for a future streaming body kind and must be null for buffered responses. - */ -struct CustomSchemeResponse { - static constexpr uint32_t CurrentAbiVersion = 1; - static constexpr uint64_t MaxBufferedBodyBytes = 256ULL * 1024ULL * 1024ULL; - - uint32_t StructSize; - uint32_t AbiVersion; - uint32_t StatusCode; - uint32_t BodyKind; - uint64_t ContentLength; - const uint8_t* Body; - const char* ContentTypeUtf8; - void* OwnerContext; - ReleaseCustomSchemeResponseCallback Release; - void* ReservedRead; - void* ReservedSeek; -}; - -static_assert(sizeof(uintptr_t) != 8 || sizeof(CustomSchemeResponse) == 72, "Unexpected 64-bit response ABI layout"); - /** * @brief Called when the WebView requests a custom-scheme resource. * @param url Platform-native URL (UTF-8); borrowed for the duration of the call @@ -167,4 +134,4 @@ using NavigationStartingCallback = int (*)(const char* url, int isUserInitiated, * @param x Screen X coordinate of drop location * @param y Screen Y coordinate of drop location */ -using FileDroppedCallback = void (*)(const char** paths, int count, int x, int y); \ No newline at end of file +using FileDroppedCallback = void (*)(const char** paths, int count, int x, int y); diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Types/CustomSchemeResponse.h b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Types/CustomSchemeResponse.h new file mode 100644 index 000000000..5f40598cc --- /dev/null +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Types/CustomSchemeResponse.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +/** Version 1 custom-scheme response body kinds. Kind 2 is reserved for streaming. */ +enum class CustomSchemeBodyKind : uint32_t { + Buffered = 1, + Stream = 2 +}; + +using ReleaseCustomSchemeResponseCallback = void (*)(void* ownerContext); + +/** Versioned response descriptor shared with the managed ABI. */ +struct CustomSchemeResponse { + static constexpr uint32_t CurrentAbiVersion = 1; + static constexpr uint64_t MaxBufferedBodyBytes = 256ULL * 1024ULL * 1024ULL; + + uint32_t StructSize; + uint32_t AbiVersion; + uint32_t StatusCode; + uint32_t BodyKind; + uint64_t ContentLength; + const uint8_t* Body; + const char* ContentTypeUtf8; + void* OwnerContext; + ReleaseCustomSchemeResponseCallback Release; + void* ReservedRead; + void* ReservedSeek; +}; + +static_assert(sizeof(uintptr_t) != 8 || sizeof(CustomSchemeResponse) == 72, + "Unexpected 64-bit response ABI layout"); diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Types/DialogButtons.h b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Types/DialogButtons.h similarity index 100% rename from src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Types/DialogButtons.h rename to src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Types/DialogButtons.h diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Types/DialogIcon.h b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Types/DialogIcon.h similarity index 100% rename from src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Types/DialogIcon.h rename to src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Types/DialogIcon.h diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Types/DialogResult.h b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Types/DialogResult.h similarity index 100% rename from src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Types/DialogResult.h rename to src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Types/DialogResult.h diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Types/InfiniFrameApplicationInitParams.h b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Types/InfiniFrameApplicationInitParams.h new file mode 100644 index 000000000..5b4b39c36 --- /dev/null +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Types/InfiniFrameApplicationInitParams.h @@ -0,0 +1,24 @@ +#pragma once +// --------------------------------------------------------------------------------------------------------------------- +// Imports +// --------------------------------------------------------------------------------------------------------------------- +#include +// --------------------------------------------------------------------------------------------------------------------- +// Code +// --------------------------------------------------------------------------------------------------------------------- +/** + * @brief Process-wide initialization parameters for InfiniFrameApplication. + * + * Field order defines the ABI layout shared with the managed + * InfiniFrameNativeApplicationParameters type. New fields must be appended + * before StructSize and the managed/native layouts must be updated together. + */ +struct InfiniFrameApplicationInitParams { + const char* WebView2RuntimePath; + const char* NotificationRegistrationId; + const char* AppUserModelId; + const char* DefaultNotificationIcon; + + // ABI version/size marker. This field must remain last. + int StructSize; +}; diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Window/InfiniFrameWindow.h b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Types/InfiniFrameWindow.h similarity index 96% rename from src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Window/InfiniFrameWindow.h rename to src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Types/InfiniFrameWindow.h index 4c8da0105..6e8cc6c29 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Window/InfiniFrameWindow.h +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Types/InfiniFrameWindow.h @@ -2,46 +2,53 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -#ifdef _WIN32 -#include -#include -#include -#endif - -#ifdef __APPLE__ -#include -#include -#include -#include -#include -#include -#include -#endif - -#ifdef __linux__ -#include -#include -#endif - -#include +#include #include +#include #include -#include "Runtime/Shared/Types/Basic.h" -#include "Runtime/Shared/Types/DialogButtons.h" -#include "Runtime/Shared/Types/DialogIcon.h" -#include "Runtime/Shared/Types/DialogResult.h" -#include "Runtime/Shared/Types/Callbacks.h" +#include "Runtime/Internal/Interop/Types/Basic.h" +#include "Runtime/Internal/Interop/Types/DialogButtons.h" +#include "Runtime/Internal/Interop/Types/DialogIcon.h" +#include "Runtime/Internal/Interop/Types/DialogResult.h" +#include "Runtime/Internal/Interop/Types/Callbacks.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- #ifdef _WIN32 class WinToastHandler; +#ifndef _WINDEF_ +struct HWND__; +struct HINSTANCE__; +using HWND = HWND__*; +using HINSTANCE = HINSTANCE__*; +using WPARAM = uintptr_t; +using LPARAM = intptr_t; +using LRESULT = intptr_t; +using UINT = unsigned int; +#endif +#ifndef _WINDEF_ +using HRESULT = long; +#endif +struct ICoreWebView2EnvironmentOptions; +#endif +#ifdef __linux__ +typedef enum _GdkWindowState GdkWindowState; +struct _GtkWidget; +using GtkWidget = _GtkWidget; +#endif +#ifdef __APPLE__ +#ifdef __OBJC__ +@class NSWindow; +#else +class NSWindow; +#endif #endif class InfiniFrameDialog; -struct InfiniFrameInitParams; +class InfiniFrameApplication; +struct InfiniFrameWindowInitParams; +struct CommonWindowState; -struct InfiniFrameWindowImpl; struct NativeOperation; struct DialogOperation; enum class NativeOperationResult : int32_t; @@ -58,7 +65,7 @@ class InfiniFrameWindow { * @brief Construct new InfiniFrame window * @param initParams Initialization parameters */ - explicit InfiniFrameWindow(InfiniFrameInitParams* initParams); + explicit InfiniFrameWindow(InfiniFrameWindowInitParams* initParams); /** * @brief Destroy InfiniFrame window @@ -696,6 +703,7 @@ class InfiniFrameWindow { /** @brief Tear down the WebView control while keeping the native window alive */ void CloseWebView(); + [[nodiscard]] InfiniFrameApplication* GetApplication() const noexcept { return _application; } #ifdef __APPLE__ /** @@ -838,8 +846,10 @@ class InfiniFrameWindow { /** Platform-specific non-blocking event-loop enqueue. */ bool ScheduleOperation(const std::shared_ptr& operation); void SetReadyCallback(ContextAction callback, void* context); + void SetReadyFailureCallback(ContextAction callback, void* context); void SetTeardownCallback(ContextAction callback, void* context); void SignalReady(); + void SignalReadyFailure(); void SignalTeardown(); /// Schedule the teardown completion callback to run after pending work finishes. void ScheduleTeardownCompletion(); @@ -1068,6 +1078,9 @@ class InfiniFrameWindow { struct Impl; private: + friend CommonWindowState* GetCommonWindowState(InfiniFrameWindow* window) noexcept; + friend const CommonWindowState* GetCommonWindowState(const InfiniFrameWindow* window) noexcept; + friend struct InfiniFrameWindowAccess; void Show(bool isAlreadyShown); void AttachWebView(); @@ -1079,14 +1092,6 @@ class InfiniFrameWindow { HRESULT ApplyInitialWebViewSettings(); #endif -#ifdef _WIN32 - friend LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); -#endif - - InfiniFrameWindowImpl* ImplBase() noexcept; - const InfiniFrameWindowImpl* ImplBase() const noexcept; - std::unique_ptr m_impl; + InfiniFrameApplication* _application = nullptr; }; - -#include "InfiniFrameInitParams.h" \ No newline at end of file diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Window/InfiniFrameInitParams.h b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Types/InfiniFrameWindowInitParams.h similarity index 95% rename from src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Window/InfiniFrameInitParams.h rename to src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Types/InfiniFrameWindowInitParams.h index 1c7beb92b..078b2d455 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Window/InfiniFrameInitParams.h +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Types/InfiniFrameWindowInitParams.h @@ -2,12 +2,13 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -#include "Runtime/Shared/Types/Basic.h" -#include "Runtime/Shared/Types/Callbacks.h" +#include "Runtime/Internal/Interop/Types/Basic.h" +#include "Runtime/Internal/Interop/Types/Callbacks.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- class InfiniFrameWindow; // Forward declaration +class InfiniFrameApplication; // Forward declaration /** * @brief Initialization parameters for InfiniFrame window. @@ -15,7 +16,7 @@ class InfiniFrameWindow; // Forward declaration * Field order defines the ABI layout shared with the managed (.NET) side via LayoutKind.Sequential. * When adding or removing fields, append at the end (before StructSize) and bump StructSize. */ -struct InfiniFrameInitParams { +struct InfiniFrameWindowInitParams { static constexpr std::size_t MaxCustomSchemeNames = 16; // ── Content strings ──────────────────────────────────────────────────── @@ -28,9 +29,6 @@ struct InfiniFrameInitParams { const char* TemporaryFilesPath; const char* UserAgent; const char* BrowserControlInitParameters; - const char* WebView2RuntimePath; - const char* NotificationRegistrationId; - const char* WindowsAppUserModelId; const char* DefaultNotificationIcon; // ── Runtime configuration ────────────────────────────────────────────── @@ -38,6 +36,7 @@ struct InfiniFrameInitParams { // ── Parent window ────────────────────────────────────────────────────── InfiniFrameWindow* ParentInstance; + InfiniFrameApplication* ApplicationInstance; // ── Event callbacks ──────────────────────────────────────────────────── ClosingCallback ClosingHandler; @@ -110,4 +109,4 @@ struct InfiniFrameInitParams { // ── ABI version (must remain last) ───────────────────────────────────── int StructSize; -}; \ No newline at end of file +}; diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Utilities/InteropStatus.h b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Types/InteropStatus.h similarity index 100% rename from src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Utilities/InteropStatus.h rename to src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Types/InteropStatus.h diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Types/Monitor.h b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Types/Monitor.h similarity index 100% rename from src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Types/Monitor.h rename to src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Interop/Types/Monitor.h diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Operations/DialogOperation.cpp b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Operations/DialogOperation.cpp similarity index 77% rename from src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Operations/DialogOperation.cpp rename to src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Operations/DialogOperation.cpp index 0079135c9..3541949f5 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Operations/DialogOperation.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Operations/DialogOperation.cpp @@ -1,7 +1,7 @@ -#include "Runtime/Shared/Operations/DialogOperation.h" +#include "Runtime/Internal/Operations/DialogOperation.h" -#include "Runtime/Shared/Window/InfiniFrameWindow.h" -#include "Runtime/Shared/Window/InfiniFrameWindowImpl.h" +#include "Runtime/Internal/Interop/Types/InfiniFrameWindow.h" +#include "Runtime/Internal/Window/CommonWindowState.h" #include @@ -80,14 +80,14 @@ std::shared_ptr InfiniFrameWindow::RegisterFileDialogOperation( void* context ) { auto operation = std::make_shared(id, name, completion, context); - std::lock_guard lock(ImplBase()->_dialogOperationMutex); - for (auto it = ImplBase()->_dialogOperations.begin(); it != ImplBase()->_dialogOperations.end();) { + std::lock_guard lock(GetCommonWindowState(this)->_dialogOperationMutex); + for (auto it = GetCommonWindowState(this)->_dialogOperations.begin(); it != GetCommonWindowState(this)->_dialogOperations.end();) { if (it->second->terminal.load(std::memory_order_acquire)) - it = ImplBase()->_dialogOperations.erase(it); + it = GetCommonWindowState(this)->_dialogOperations.erase(it); else ++it; } - if (!ImplBase()->_dialogOperations.emplace(id, operation).second) + if (!GetCommonWindowState(this)->_dialogOperations.emplace(id, operation).second) throw std::invalid_argument("A dialog operation with this ID already exists."); return operation; } @@ -98,8 +98,8 @@ std::shared_ptr InfiniFrameWindow::RegisterMessageDialogOperati void* context ) { auto operation = std::make_shared(id, "ShowMessage", completion, context); - std::lock_guard lock(ImplBase()->_dialogOperationMutex); - if (!ImplBase()->_dialogOperations.emplace(id, operation).second) + std::lock_guard lock(GetCommonWindowState(this)->_dialogOperationMutex); + if (!GetCommonWindowState(this)->_dialogOperations.emplace(id, operation).second) throw std::invalid_argument("A dialog operation with this ID already exists."); return operation; } @@ -107,9 +107,9 @@ std::shared_ptr InfiniFrameWindow::RegisterMessageDialogOperati bool InfiniFrameWindow::CancelDialog(const uint64_t id) { std::shared_ptr operation; { - std::lock_guard lock(ImplBase()->_dialogOperationMutex); - const auto found = ImplBase()->_dialogOperations.find(id); - if (found == ImplBase()->_dialogOperations.end()) + std::lock_guard lock(GetCommonWindowState(this)->_dialogOperationMutex); + const auto found = GetCommonWindowState(this)->_dialogOperations.find(id); + if (found == GetCommonWindowState(this)->_dialogOperations.end()) return false; operation = found->second; } @@ -119,11 +119,11 @@ bool InfiniFrameWindow::CancelDialog(const uint64_t id) { void InfiniFrameWindow::CompleteDialogsForClose() { std::vector> operations; { - std::lock_guard lock(ImplBase()->_dialogOperationMutex); - for (const auto& [id, operation] : ImplBase()->_dialogOperations) + std::lock_guard lock(GetCommonWindowState(this)->_dialogOperationMutex); + for (const auto& [id, operation] : GetCommonWindowState(this)->_dialogOperations) operations.push_back(operation); - ImplBase()->_dialogOperations.clear(); + GetCommonWindowState(this)->_dialogOperations.clear(); } for (const auto& operation : operations) operation->Cancel(NativeOperationResult::WindowClosed); -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Operations/DialogOperation.h b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Operations/DialogOperation.h similarity index 90% rename from src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Operations/DialogOperation.h rename to src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Operations/DialogOperation.h index 8c58da18f..117b51f90 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Operations/DialogOperation.h +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Operations/DialogOperation.h @@ -7,9 +7,9 @@ #include #include -#include "Runtime/Shared/Operations/NativeOperation.h" -#include "Runtime/Shared/Types/Callbacks.h" -#include "Runtime/Shared/Types/DialogResult.h" +#include "Runtime/Internal/Operations/NativeOperation.h" +#include "Runtime/Internal/Interop/Types/Callbacks.h" +#include "Runtime/Internal/Interop/Types/DialogResult.h" /** Window-owned state shared with an asynchronous native dialog callback. */ struct DialogOperation final { @@ -51,4 +51,4 @@ struct DialogOperation final { private: std::mutex _cancelMutex; std::function _cancelAction; -}; \ No newline at end of file +}; diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Operations/NativeOperation.cpp b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Operations/NativeOperation.cpp similarity index 57% rename from src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Operations/NativeOperation.cpp rename to src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Operations/NativeOperation.cpp index da8d8c799..51fe561d3 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Operations/NativeOperation.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Operations/NativeOperation.cpp @@ -1,7 +1,7 @@ -#include "Runtime/Shared/Operations/NativeOperation.h" +#include "Runtime/Internal/Operations/NativeOperation.h" -#include "Runtime/Shared/Window/InfiniFrameWindow.h" -#include "Runtime/Shared/Window/InfiniFrameWindowImpl.h" +#include "Runtime/Internal/Interop/Types/InfiniFrameWindow.h" +#include "Runtime/Internal/Window/CommonWindowState.h" #include @@ -50,8 +50,8 @@ bool InfiniFrameWindow::BeginInvoke( operationId, callback, callbackContext, completion, completionContext, this ); { - std::lock_guard lock(ImplBase()->_operationMutex); - if (!ImplBase()->_operations.emplace(operationId, operation).second) + std::lock_guard lock(GetCommonWindowState(this)->_operationMutex); + if (!GetCommonWindowState(this)->_operations.emplace(operationId, operation).second) return false; } @@ -65,9 +65,9 @@ bool InfiniFrameWindow::BeginInvoke( bool InfiniFrameWindow::CancelOperation(const uint64_t operationId, const NativeOperationResult result) { std::shared_ptr operation; { - std::lock_guard lock(ImplBase()->_operationMutex); - const auto found = ImplBase()->_operations.find(operationId); - if (found == ImplBase()->_operations.end()) + std::lock_guard lock(GetCommonWindowState(this)->_operationMutex); + const auto found = GetCommonWindowState(this)->_operations.find(operationId); + if (found == GetCommonWindowState(this)->_operations.end()) return false; operation = found->second; } @@ -82,16 +82,16 @@ void InfiniFrameWindow::CompleteOperationsForClose() { }; std::vector completions; { - std::lock_guard lock(ImplBase()->_operationMutex); - completions.reserve(ImplBase()->_operations.size()); - for (const auto& [id, operation] : ImplBase()->_operations) { + std::lock_guard lock(GetCommonWindowState(this)->_operationMutex); + completions.reserve(GetCommonWindowState(this)->_operations.size()); + for (const auto& [id, operation] : GetCommonWindowState(this)->_operations) { int expected = NativeOperation::Pending; if (operation && operation->state.compare_exchange_strong( expected, NativeOperation::Terminal, std::memory_order_acq_rel)) { completions.push_back({id, operation->completion, operation->completionContext}); } } - ImplBase()->_operations.clear(); + GetCommonWindowState(this)->_operations.clear(); } // All window-owned state is detached before the first reverse callback. A callback may @@ -113,8 +113,8 @@ void InfiniFrameWindow::FinalizeOperation( const char* failure ) noexcept { { - std::lock_guard lock(ImplBase()->_operationMutex); - ImplBase()->_operations.erase(operationId); + std::lock_guard lock(GetCommonWindowState(this)->_operationMutex); + GetCommonWindowState(this)->_operations.erase(operationId); } // This reverse callback can dispose the managed window. It must be the final access @@ -125,10 +125,10 @@ void InfiniFrameWindow::FinalizeOperation( void InfiniFrameWindow::SetReadyCallback(const ContextAction callback, void* context) { bool invoke = false; { - std::lock_guard lock(ImplBase()->_milestoneMutex); - ImplBase()->_readyCallback = callback; - ImplBase()->_readyCallbackContext = context; - invoke = ImplBase()->_readySignaled && callback != nullptr; + std::lock_guard lock(GetCommonWindowState(this)->_milestoneMutex); + GetCommonWindowState(this)->_readyCallback = callback; + GetCommonWindowState(this)->_readyCallbackContext = context; + invoke = GetCommonWindowState(this)->_readySignaled && callback != nullptr; } if (invoke) callback(context); @@ -137,10 +137,22 @@ void InfiniFrameWindow::SetReadyCallback(const ContextAction callback, void* con void InfiniFrameWindow::SetTeardownCallback(const ContextAction callback, void* context) { bool invoke = false; { - std::lock_guard lock(ImplBase()->_milestoneMutex); - ImplBase()->_teardownCallback = callback; - ImplBase()->_teardownCallbackContext = context; - invoke = ImplBase()->_teardownSignaled && callback != nullptr; + std::lock_guard lock(GetCommonWindowState(this)->_milestoneMutex); + GetCommonWindowState(this)->_teardownCallback = callback; + GetCommonWindowState(this)->_teardownCallbackContext = context; + invoke = GetCommonWindowState(this)->_teardownSignaled && callback != nullptr; + } + if (invoke) + callback(context); +} + +void InfiniFrameWindow::SetReadyFailureCallback(const ContextAction callback, void* context) { + bool invoke = false; + { + std::lock_guard lock(GetCommonWindowState(this)->_milestoneMutex); + GetCommonWindowState(this)->_readyFailureCallback = callback; + GetCommonWindowState(this)->_readyFailureCallbackContext = context; + invoke = GetCommonWindowState(this)->_readySignaled && callback != nullptr; } if (invoke) callback(context); @@ -150,12 +162,27 @@ void InfiniFrameWindow::SignalReady() { ContextAction callback = nullptr; void* context = nullptr; { - std::lock_guard lock(ImplBase()->_milestoneMutex); - if (ImplBase()->_readySignaled) + std::lock_guard lock(GetCommonWindowState(this)->_milestoneMutex); + if (GetCommonWindowState(this)->_readySignaled) return; - ImplBase()->_readySignaled = true; - callback = ImplBase()->_readyCallback; - context = ImplBase()->_readyCallbackContext; + GetCommonWindowState(this)->_readySignaled = true; + callback = GetCommonWindowState(this)->_readyCallback; + context = GetCommonWindowState(this)->_readyCallbackContext; + } + if (callback != nullptr) + callback(context); +} + +void InfiniFrameWindow::SignalReadyFailure() { + ContextAction callback = nullptr; + void* context = nullptr; + { + std::lock_guard lock(GetCommonWindowState(this)->_milestoneMutex); + if (GetCommonWindowState(this)->_readySignaled) + return; + GetCommonWindowState(this)->_readySignaled = true; + callback = GetCommonWindowState(this)->_readyFailureCallback; + context = GetCommonWindowState(this)->_readyFailureCallbackContext; } if (callback != nullptr) callback(context); @@ -165,12 +192,12 @@ void InfiniFrameWindow::SignalTeardown() { ContextAction callback = nullptr; void* context = nullptr; { - std::lock_guard lock(ImplBase()->_milestoneMutex); - if (ImplBase()->_teardownSignaled) + std::lock_guard lock(GetCommonWindowState(this)->_milestoneMutex); + if (GetCommonWindowState(this)->_teardownSignaled) return; - ImplBase()->_teardownSignaled = true; - callback = ImplBase()->_teardownCallback; - context = ImplBase()->_teardownCallbackContext; + GetCommonWindowState(this)->_teardownSignaled = true; + callback = GetCommonWindowState(this)->_teardownCallback; + context = GetCommonWindowState(this)->_teardownCallbackContext; } if (callback != nullptr) callback(context); @@ -199,9 +226,9 @@ bool InfiniFrameWindow::BeginNavigateToString( ) { std::unique_ptr superseded; { - std::lock_guard lock(ImplBase()->_navigationMutex); - superseded = std::move(ImplBase()->_navigationOperation); - ImplBase()->_navigationOperation = std::make_unique( + std::lock_guard lock(GetCommonWindowState(this)->_navigationMutex); + superseded = std::move(GetCommonWindowState(this)->_navigationOperation); + GetCommonWindowState(this)->_navigationOperation = std::make_unique( NavigationOperation{ operationId, 0, completion, completionContext }); @@ -219,9 +246,9 @@ bool InfiniFrameWindow::BeginNavigateToUrl( ) { std::unique_ptr superseded; { - std::lock_guard lock(ImplBase()->_navigationMutex); - superseded = std::move(ImplBase()->_navigationOperation); - ImplBase()->_navigationOperation = std::make_unique( + std::lock_guard lock(GetCommonWindowState(this)->_navigationMutex); + superseded = std::move(GetCommonWindowState(this)->_navigationOperation); + GetCommonWindowState(this)->_navigationOperation = std::make_unique( NavigationOperation{ operationId, 0, completion, completionContext }); @@ -234,19 +261,19 @@ bool InfiniFrameWindow::BeginNavigateToUrl( bool InfiniFrameWindow::CancelNavigation(const uint64_t operationId) { std::unique_ptr cancelled; { - std::lock_guard lock(ImplBase()->_navigationMutex); - if (!ImplBase()->_navigationOperation || ImplBase()->_navigationOperation->id != operationId) + std::lock_guard lock(GetCommonWindowState(this)->_navigationMutex); + if (!GetCommonWindowState(this)->_navigationOperation || GetCommonWindowState(this)->_navigationOperation->id != operationId) return false; - cancelled = std::move(ImplBase()->_navigationOperation); + cancelled = std::move(GetCommonWindowState(this)->_navigationOperation); } CompleteDetachedNavigation(std::move(cancelled), NativeOperationResult::Cancelled); return true; } void InfiniFrameWindow::BindNavigationBackendId(const uint64_t backendId) { - std::lock_guard lock(ImplBase()->_navigationMutex); - if (ImplBase()->_navigationOperation && ImplBase()->_navigationOperation->backendId == 0) - ImplBase()->_navigationOperation->backendId = backendId; + std::lock_guard lock(GetCommonWindowState(this)->_navigationMutex); + if (GetCommonWindowState(this)->_navigationOperation && GetCommonWindowState(this)->_navigationOperation->backendId == 0) + GetCommonWindowState(this)->_navigationOperation->backendId = backendId; } void InfiniFrameWindow::CompleteNavigation( @@ -257,13 +284,13 @@ void InfiniFrameWindow::CompleteNavigation( ) { std::unique_ptr completed; { - std::lock_guard lock(ImplBase()->_navigationMutex); - if (!ImplBase()->_navigationOperation) + std::lock_guard lock(GetCommonWindowState(this)->_navigationMutex); + if (!GetCommonWindowState(this)->_navigationOperation) return; - if (backendId != 0 && ImplBase()->_navigationOperation->backendId != 0 - && backendId != ImplBase()->_navigationOperation->backendId) + if (backendId != 0 && GetCommonWindowState(this)->_navigationOperation->backendId != 0 + && backendId != GetCommonWindowState(this)->_navigationOperation->backendId) return; - completed = std::move(ImplBase()->_navigationOperation); + completed = std::move(GetCommonWindowState(this)->_navigationOperation); } CompleteDetachedNavigation( std::move(completed), @@ -282,19 +309,19 @@ void InfiniFrameWindow::CompleteNavigationAndSignalReady( ContextAction readyCallback = nullptr; void* readyContext = nullptr; { - std::lock_guard lock(ImplBase()->_navigationMutex); - if (ImplBase()->_navigationOperation - && (backendId == 0 || ImplBase()->_navigationOperation->backendId == 0 - || backendId == ImplBase()->_navigationOperation->backendId)) { - completed = std::move(ImplBase()->_navigationOperation); + std::lock_guard lock(GetCommonWindowState(this)->_navigationMutex); + if (GetCommonWindowState(this)->_navigationOperation + && (backendId == 0 || GetCommonWindowState(this)->_navigationOperation->backendId == 0 + || backendId == GetCommonWindowState(this)->_navigationOperation->backendId)) { + completed = std::move(GetCommonWindowState(this)->_navigationOperation); } } { - std::lock_guard lock(ImplBase()->_milestoneMutex); - if (!ImplBase()->_readySignaled) { - ImplBase()->_readySignaled = true; - readyCallback = ImplBase()->_readyCallback; - readyContext = ImplBase()->_readyCallbackContext; + std::lock_guard lock(GetCommonWindowState(this)->_milestoneMutex); + if (!GetCommonWindowState(this)->_readySignaled) { + GetCommonWindowState(this)->_readySignaled = true; + readyCallback = GetCommonWindowState(this)->_readyCallback; + readyContext = GetCommonWindowState(this)->_readyCallbackContext; } } @@ -312,8 +339,8 @@ void InfiniFrameWindow::CompleteNavigationAndSignalReady( void InfiniFrameWindow::CompleteNavigationForClose() { std::unique_ptr completed; { - std::lock_guard lock(ImplBase()->_navigationMutex); - completed = std::move(ImplBase()->_navigationOperation); + std::lock_guard lock(GetCommonWindowState(this)->_navigationMutex); + completed = std::move(GetCommonWindowState(this)->_navigationOperation); } CompleteDetachedNavigation(std::move(completed), NativeOperationResult::WindowClosed); -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Operations/NativeOperation.h b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Operations/NativeOperation.h similarity index 98% rename from src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Operations/NativeOperation.h rename to src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Operations/NativeOperation.h index fa9bb1375..13ccc77f7 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Operations/NativeOperation.h +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Operations/NativeOperation.h @@ -4,7 +4,7 @@ #include #include -#include "Runtime/Shared/Types/Callbacks.h" +#include "Runtime/Internal/Interop/Types/Callbacks.h" class InfiniFrameWindow; diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Operations/NavigationOperation.h b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Operations/NavigationOperation.h similarity index 90% rename from src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Operations/NavigationOperation.h rename to src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Operations/NavigationOperation.h index ee1e8cbe8..518eeaa11 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Operations/NavigationOperation.h +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Operations/NavigationOperation.h @@ -2,7 +2,7 @@ #include -#include "Runtime/Shared/Types/Callbacks.h" +#include "Runtime/Internal/Interop/Types/Callbacks.h" /// Represents an in-flight navigation operation (load URL or load string). struct NavigationOperation final { diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Utilities/Dimensions.h b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Utilities/Dimensions.h similarity index 100% rename from src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Utilities/Dimensions.h rename to src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Utilities/Dimensions.h diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Utilities/ErrorCode.h b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Utilities/ErrorCode.h similarity index 100% rename from src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Utilities/ErrorCode.h rename to src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Utilities/ErrorCode.h diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Utilities/StringArrayCopy.h b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Utilities/StringArrayCopy.h similarity index 97% rename from src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Utilities/StringArrayCopy.h rename to src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Utilities/StringArrayCopy.h index 5cbb9fcb6..d5597fcc3 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Utilities/StringArrayCopy.h +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Utilities/StringArrayCopy.h @@ -5,7 +5,7 @@ #include #include -#include "Runtime/Shared/Utilities/StringCopy.h" +#include "Runtime/Internal/Utilities/StringCopy.h" // --------------------------------------------------------------------------------------------------------------------- // Owned string array allocation and free, consistent with StringCopy.h. // @@ -52,4 +52,4 @@ inline void FreeStringArray(const char** arr, const int count) { } } delete[] arr; -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Utilities/StringCopy.h b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Utilities/StringCopy.h similarity index 92% rename from src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Utilities/StringCopy.h rename to src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Utilities/StringCopy.h index 96619c249..25a3830e5 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Utilities/StringCopy.h +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Utilities/StringCopy.h @@ -5,6 +5,9 @@ #include #include #include +#ifdef _WIN32 +#include +#endif // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -36,4 +39,8 @@ inline char* AllocateUtf8FromWide(const std::wstring& wstr) { copy[utf8Count] = '\0'; return copy; } + +inline char* AllocateStringCopy(const std::wstring& str) { + return AllocateUtf8FromWide(str); +} #endif diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/WebView/CustomSchemeResponse.h b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/WebView/CustomSchemeResponse.h similarity index 99% rename from src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/WebView/CustomSchemeResponse.h rename to src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/WebView/CustomSchemeResponse.h index 83e7bd67c..62e04bccd 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/WebView/CustomSchemeResponse.h +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/WebView/CustomSchemeResponse.h @@ -7,7 +7,7 @@ #include #include -#include "Runtime/Shared/Types/Callbacks.h" +#include "Runtime/Internal/Interop/Types/CustomSchemeResponse.h" // --------------------------------------------------------------------------------------------------------------------- // Cross-platform CORS/header helper for custom-scheme responses. // @@ -173,4 +173,4 @@ namespace infiniframe { } return h; } -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Window/InfiniFrameWindowImpl.h b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Window/CommonWindowState.h similarity index 83% rename from src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Window/InfiniFrameWindowImpl.h rename to src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Window/CommonWindowState.h index 6ee4873b2..eeb2ca6fb 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Window/InfiniFrameWindowImpl.h +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Window/CommonWindowState.h @@ -8,18 +8,25 @@ #include #include -#include "Runtime/Shared/Types/Basic.h" -#include "Runtime/Shared/Types/Callbacks.h" -#include "Runtime/Shared/Window/InfiniFrameDialog.h" -#include "Runtime/Shared/Operations/NativeOperation.h" -#include "Runtime/Shared/Operations/NavigationOperation.h" -#include "Runtime/Shared/Operations/DialogOperation.h" +#include "Runtime/Internal/Interop/Types/Basic.h" +#include "Runtime/Internal/Interop/Types/Callbacks.h" +#include "Runtime/Internal/Window/InfiniFrameDialog.h" +#include "Runtime/Internal/Operations/NativeOperation.h" +#include "Runtime/Internal/Operations/NavigationOperation.h" +#include "Runtime/Internal/Operations/DialogOperation.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- class InfiniFrameWindow; +struct CommonWindowState; -struct InfiniFrameWindowImpl { +CommonWindowState* GetCommonWindowState(InfiniFrameWindow* window) noexcept; +const CommonWindowState* GetCommonWindowState(const InfiniFrameWindow* window) noexcept; + + +// Shared logical state. Platform backends own this state through their private +// implementation object; it is never part of the public window declaration. +struct CommonWindowState { std::mutex _operationMutex; std::unordered_map> _operations; std::mutex _navigationMutex; @@ -29,9 +36,12 @@ struct InfiniFrameWindowImpl { std::mutex _milestoneMutex; ContextAction _readyCallback = nullptr; void* _readyCallbackContext = nullptr; + ContextAction _readyFailureCallback = nullptr; + void* _readyFailureCallbackContext = nullptr; ContextAction _teardownCallback = nullptr; void* _teardownCallbackContext = nullptr; bool _readySignaled = false; + bool _readySucceeded = false; bool _teardownSignaled = false; // ----------------------------------------------------------------------------------------------------------------- // Callbacks @@ -95,4 +105,4 @@ struct InfiniFrameWindowImpl { // ----------------------------------------------------------------------------------------------------------------- InfiniFrameWindow* _parent = nullptr; std::unique_ptr _dialog; -}; \ No newline at end of file +}; diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Window/InfiniFrameDialog.h b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Window/InfiniFrameDialog.h similarity index 87% rename from src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Window/InfiniFrameDialog.h rename to src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Window/InfiniFrameDialog.h index e17dbf149..bb0f884a9 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Window/InfiniFrameDialog.h +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Window/InfiniFrameDialog.h @@ -1,15 +1,12 @@ #pragma once +#include // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -#ifdef __APPLE__ -#include -#endif - -#include "Runtime/Shared/Types/Basic.h" -#include "Runtime/Shared/Types/DialogButtons.h" -#include "Runtime/Shared/Types/DialogIcon.h" -#include "Runtime/Shared/Types/DialogResult.h" +#include "Runtime/Internal/Interop/Types/Basic.h" +#include "Runtime/Internal/Interop/Types/DialogButtons.h" +#include "Runtime/Internal/Interop/Types/DialogIcon.h" +#include "Runtime/Internal/Interop/Types/DialogResult.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -94,13 +91,7 @@ class InfiniFrameDialog { */ DialogResult ShowMessage(const char* title, const char* text, DialogButtons buttons, DialogIcon icon); - protected: -#ifdef __APPLE__ - NSImage* _errorIcon; - NSImage* _infoIcon; - NSImage* _questionIcon; - NSImage* _warningIcon; -#elif _WIN32 - InfiniFrameWindow* _window; -#endif -}; \ No newline at end of file + private: + struct Impl; + std::unique_ptr m_impl; +}; diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Window/WindowEvents.cpp b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Window/WindowEvents.cpp similarity index 59% rename from src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Window/WindowEvents.cpp rename to src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Window/WindowEvents.cpp index eff080a96..c68c98ef3 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Window/WindowEvents.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Window/WindowEvents.cpp @@ -1,113 +1,113 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -#include "Runtime/Shared/Window/InfiniFrameWindow.h" -#include "Runtime/Shared/Window/InfiniFrameWindowImpl.h" +#include "Runtime/Internal/Interop/Types/InfiniFrameWindow.h" +#include "Runtime/Internal/Window/CommonWindowState.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- InfiniFrameDialog* InfiniFrameWindow::GetDialog() const { - return ImplBase()->_dialog.get(); + return GetCommonWindowState(this)->_dialog.get(); } // ----------------------------------------------------------------------------------------------------------------- // Callback Setters // ----------------------------------------------------------------------------------------------------------------- void InfiniFrameWindow::SetClosingCallback(const ClosingCallback callback) { - ImplBase()->_closingCallback = callback; + GetCommonWindowState(this)->_closingCallback = callback; } void InfiniFrameWindow::SetClosedCallback(const ClosedCallback callback) { - ImplBase()->_closedCallback = callback; + GetCommonWindowState(this)->_closedCallback = callback; } void InfiniFrameWindow::SetFocusInCallback(const FocusInCallback callback) { - ImplBase()->_focusInCallback = callback; + GetCommonWindowState(this)->_focusInCallback = callback; } void InfiniFrameWindow::SetFocusOutCallback(const FocusOutCallback callback) { - ImplBase()->_focusOutCallback = callback; + GetCommonWindowState(this)->_focusOutCallback = callback; } void InfiniFrameWindow::SetMovedCallback(const MovedCallback callback) { - ImplBase()->_movedCallback = callback; + GetCommonWindowState(this)->_movedCallback = callback; } void InfiniFrameWindow::SetResizedCallback(const ResizedCallback callback) { - ImplBase()->_resizedCallback = callback; + GetCommonWindowState(this)->_resizedCallback = callback; } void InfiniFrameWindow::SetMaximizedCallback(const MaximizedCallback callback) { - ImplBase()->_maximizedCallback = callback; + GetCommonWindowState(this)->_maximizedCallback = callback; } void InfiniFrameWindow::SetRestoredCallback(const RestoredCallback callback) { - ImplBase()->_restoredCallback = callback; + GetCommonWindowState(this)->_restoredCallback = callback; } void InfiniFrameWindow::SetMinimizedCallback(const MinimizedCallback callback) { - ImplBase()->_minimizedCallback = callback; + GetCommonWindowState(this)->_minimizedCallback = callback; } void InfiniFrameWindow::SetDebugEventCallback(const DebugEventCallback callback) { - ImplBase()->_debugEventCallback = callback; + GetCommonWindowState(this)->_debugEventCallback = callback; } void InfiniFrameWindow::SetFileDroppedCallback(const FileDroppedCallback callback) { - ImplBase()->_fileDroppedCallback = callback; + GetCommonWindowState(this)->_fileDroppedCallback = callback; } void InfiniFrameWindow::SetDragDropEnabled(const bool enabled) { - ImplBase()->_dragDropEnabled = enabled; + GetCommonWindowState(this)->_dragDropEnabled = enabled; } // ----------------------------------------------------------------------------------------------------------------- // Callback Invokers // ----------------------------------------------------------------------------------------------------------------- bool InfiniFrameWindow::InvokeClose() const noexcept { - if (ImplBase()->_closingCallback) - return ImplBase()->_closingCallback(); + if (GetCommonWindowState(this)->_closingCallback) + return GetCommonWindowState(this)->_closingCallback(); return false; } void InfiniFrameWindow::InvokeClosed() const noexcept { - if (ImplBase()->_closedCallback) - ImplBase()->_closedCallback(); + if (GetCommonWindowState(this)->_closedCallback) + GetCommonWindowState(this)->_closedCallback(); } void InfiniFrameWindow::InvokeFocusIn() const noexcept { - if (ImplBase()->_focusInCallback) - ImplBase()->_focusInCallback(); + if (GetCommonWindowState(this)->_focusInCallback) + GetCommonWindowState(this)->_focusInCallback(); } void InfiniFrameWindow::InvokeFocusOut() const noexcept { - if (ImplBase()->_focusOutCallback) - ImplBase()->_focusOutCallback(); + if (GetCommonWindowState(this)->_focusOutCallback) + GetCommonWindowState(this)->_focusOutCallback(); } void InfiniFrameWindow::InvokeMove(const int x, const int y) const noexcept { - if (ImplBase()->_movedCallback) - ImplBase()->_movedCallback(x, y); + if (GetCommonWindowState(this)->_movedCallback) + GetCommonWindowState(this)->_movedCallback(x, y); } void InfiniFrameWindow::InvokeResize(const int width, const int height) const noexcept { - if (ImplBase()->_resizedCallback) - ImplBase()->_resizedCallback(width, height); + if (GetCommonWindowState(this)->_resizedCallback) + GetCommonWindowState(this)->_resizedCallback(width, height); } void InfiniFrameWindow::InvokeMaximized() const noexcept { - if (ImplBase()->_maximizedCallback) - ImplBase()->_maximizedCallback(); + if (GetCommonWindowState(this)->_maximizedCallback) + GetCommonWindowState(this)->_maximizedCallback(); } void InfiniFrameWindow::InvokeRestored() const noexcept { - if (ImplBase()->_restoredCallback) - ImplBase()->_restoredCallback(); + if (GetCommonWindowState(this)->_restoredCallback) + GetCommonWindowState(this)->_restoredCallback(); } void InfiniFrameWindow::InvokeMinimized() const noexcept { - if (ImplBase()->_minimizedCallback) - ImplBase()->_minimizedCallback(); + if (GetCommonWindowState(this)->_minimizedCallback) + GetCommonWindowState(this)->_minimizedCallback(); } void InfiniFrameWindow::InvokeDebugEvent( @@ -119,8 +119,8 @@ void InfiniFrameWindow::InvokeDebugEvent( const int64_t timestampUnixMillisecondsUtc, const char* platformPayload ) const noexcept { - if (ImplBase()->_debugEventCallback) - ImplBase()->_debugEventCallback( + if (GetCommonWindowState(this)->_debugEventCallback) + GetCommonWindowState(this)->_debugEventCallback( kind, message, level, @@ -136,6 +136,6 @@ void InfiniFrameWindow::InvokeFileDropped( const int count, const int x, const int y) const noexcept { - if (ImplBase()->_fileDroppedCallback) - ImplBase()->_fileDroppedCallback(paths, count, x, y); -} \ No newline at end of file + if (GetCommonWindowState(this)->_fileDroppedCallback) + GetCommonWindowState(this)->_fileDroppedCallback(paths, count, x, y); +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Window/WindowState.cpp b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Window/WindowState.cpp similarity index 52% rename from src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Window/WindowState.cpp rename to src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Window/WindowState.cpp index b9f6d9a65..f0698e4ce 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Window/WindowState.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Internal/Window/WindowState.cpp @@ -1,72 +1,72 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -#include "Runtime/Shared/Window/InfiniFrameWindow.h" -#include "Runtime/Shared/Window/InfiniFrameWindowImpl.h" -#include "Runtime/Shared/Utilities/StringCopy.h" +#include "Runtime/Internal/Interop/Types/InfiniFrameWindow.h" +#include "Runtime/Internal/Window/CommonWindowState.h" +#include "Runtime/Internal/Utilities/StringCopy.h" // --------------------------------------------------------------------------------------------------------------------- -// Pure property getters that read from InfiniFrameWindowImpl fields with no +// Pure property getters that read from internal common state with no // platform-specific logic. Shared across all platforms. // --------------------------------------------------------------------------------------------------------------------- void InfiniFrameWindow::GetGrantBrowserPermissions(bool* grant) const { - *grant = ImplBase()->_grantBrowserPermissions; + *grant = GetCommonWindowState(this)->_grantBrowserPermissions; } const char* InfiniFrameWindow::GetUserAgent() const { #ifdef _WIN32 - return AllocateUtf8FromWide(ImplBase()->_userAgent); + return AllocateUtf8FromWide(GetCommonWindowState(this)->_userAgent); #else - return AllocateStringCopy(ImplBase()->_userAgent); + return AllocateStringCopy(GetCommonWindowState(this)->_userAgent); #endif } void InfiniFrameWindow::GetMediaAutoplayEnabled(bool* enabled) const { - *enabled = ImplBase()->_mediaAutoplayEnabled; + *enabled = GetCommonWindowState(this)->_mediaAutoplayEnabled; } void InfiniFrameWindow::GetFileSystemAccessEnabled(bool* enabled) const { - *enabled = ImplBase()->_fileSystemAccessEnabled; + *enabled = GetCommonWindowState(this)->_fileSystemAccessEnabled; } void InfiniFrameWindow::GetWebSecurityEnabled(bool* enabled) const { - *enabled = ImplBase()->_webSecurityEnabled; + *enabled = GetCommonWindowState(this)->_webSecurityEnabled; } void InfiniFrameWindow::GetJavascriptClipboardAccessEnabled(bool* enabled) const { - *enabled = ImplBase()->_javascriptClipboardAccessEnabled; + *enabled = GetCommonWindowState(this)->_javascriptClipboardAccessEnabled; } void InfiniFrameWindow::GetMediaStreamEnabled(bool* enabled) const { - *enabled = ImplBase()->_mediaStreamEnabled; + *enabled = GetCommonWindowState(this)->_mediaStreamEnabled; } void InfiniFrameWindow::GetSmoothScrollingEnabled(bool* enabled) const { - *enabled = ImplBase()->_smoothScrollingEnabled; + *enabled = GetCommonWindowState(this)->_smoothScrollingEnabled; } void InfiniFrameWindow::GetIgnoreCertificateErrorsEnabled(bool* enabled) const { - *enabled = ImplBase()->_ignoreCertificateErrorsEnabled; + *enabled = GetCommonWindowState(this)->_ignoreCertificateErrorsEnabled; } void InfiniFrameWindow::GetBrowserShortcutsEnabled(bool* enabled) const { - *enabled = ImplBase()->_browserShortcutsEnabled; + *enabled = GetCommonWindowState(this)->_browserShortcutsEnabled; } NavigationStartingCallback InfiniFrameWindow::GetNavigationStartingCallback() const { - return ImplBase()->_navigationStartingCallback; + return GetCommonWindowState(this)->_navigationStartingCallback; } const char* InfiniFrameWindow::GetIconFileName() const { #ifdef _WIN32 - return AllocateUtf8FromWide(ImplBase()->_iconFileName); + return AllocateUtf8FromWide(GetCommonWindowState(this)->_iconFileName); #else - return AllocateStringCopy(ImplBase()->_iconFileName); + return AllocateStringCopy(GetCommonWindowState(this)->_iconFileName); #endif } void InfiniFrameWindow::GetBackgroundColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const { - *r = ImplBase()->_backgroundColorR; - *g = ImplBase()->_backgroundColorG; - *b = ImplBase()->_backgroundColorB; - *a = ImplBase()->_backgroundColorA; -} \ No newline at end of file + *r = GetCommonWindowState(this)->_backgroundColorR; + *g = GetCommonWindowState(this)->_backgroundColorG; + *b = GetCommonWindowState(this)->_backgroundColorB; + *a = GetCommonWindowState(this)->_backgroundColorA; +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/Core/UiDispatcher.Gtk.cpp b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/Core/UiDispatcher.Gtk.cpp index 700f7635e..c79455cc9 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/Core/UiDispatcher.Gtk.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/Core/UiDispatcher.Gtk.cpp @@ -2,7 +2,7 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- #include "Runtime/Platform/Linux/Core/UiThread.Gtk.h" -#include "Runtime/Shared/Operations/NativeOperation.h" +#include "Runtime/Internal/Operations/NativeOperation.h" #include "Runtime/Platform/Linux/Window.Gtk.Internal.h" // --------------------------------------------------------------------------------------------------------------------- // Code @@ -23,4 +23,4 @@ bool InfiniFrameWindow::ScheduleOperation(const std::shared_ptr [operation] { operation->Execute(); }); -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/Core/WindowCore.Gtk.cpp b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/Core/WindowCore.Gtk.cpp index 0008f2abf..b0069c2dc 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/Core/WindowCore.Gtk.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/Core/WindowCore.Gtk.cpp @@ -6,20 +6,24 @@ #include "Runtime/Platform/Linux/Core/UiThread.Gtk.h" #include "Runtime/Platform/Linux/Window.Gtk.Internal.h" +#include "Runtime/Internal/Interop/Types/InfiniFrameWindowInitParams.h" +#include "Runtime/Internal/Application/InfiniFrameApplication.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- -InfiniFrameWindow::InfiniFrameWindow(InfiniFrameInitParams* initParams) : +InfiniFrameWindow::InfiniFrameWindow(InfiniFrameWindowInitParams* initParams) : m_impl(std::make_unique()) { infiniframe::linux_gtk::ui_thread::EnsureInitialized(); - if (initParams->StructSize != sizeof(InfiniFrameInitParams)) { + if (initParams->StructSize != sizeof(InfiniFrameWindowInitParams)) { throw std::invalid_argument( "Initial parameters passed are " + std::to_string(initParams->StructSize) + - " bytes, but expected " + std::to_string(sizeof(InfiniFrameInitParams)) + " bytes." + " bytes, but expected " + std::to_string(sizeof(InfiniFrameWindowInitParams)) + " bytes." ); } + _application = initParams->ApplicationInstance; + infiniframe::linux_gtk::ui_thread::InvokeSync( [this, initParams] { m_impl->InitializeFromParams(initParams); @@ -37,18 +41,23 @@ InfiniFrameWindow::InfiniFrameWindow(InfiniFrameInitParams* initParams) : if (initParams->Transparent) SetTransparentEnabled(true); - if (m_impl->_backgroundColorR != 0 || m_impl->_backgroundColorG != 0 || m_impl->_backgroundColorB != 0 || - m_impl->_backgroundColorA != 0) + if (m_impl->common._backgroundColorR != 0 || m_impl->common._backgroundColorG != 0 || m_impl->common._backgroundColorB != 0 || + m_impl->common._backgroundColorA != 0) SetBackgroundColor( - m_impl->_backgroundColorR, m_impl->_backgroundColorG, m_impl->_backgroundColorB, - m_impl->_backgroundColorA); + m_impl->common._backgroundColorR, m_impl->common._backgroundColorG, m_impl->common._backgroundColorB, + m_impl->common._backgroundColorA); - if (m_impl->_zoom != 100.0) - SetZoom(m_impl->_zoom); - }); + if (m_impl->_zoom != 100.0) + SetZoom(m_impl->_zoom); + + if (_application != nullptr) + _application->TrackWindow(this); + }); } InfiniFrameWindow::~InfiniFrameWindow() { + if (_application != nullptr) + _application->UntrackWindow(this); infiniframe::linux_gtk::ui_thread::InvokeSync( [this] { if (m_impl->_window != nullptr) { @@ -72,14 +81,14 @@ InfiniFrameWindow::~InfiniFrameWindow() { }); } -InfiniFrameWindowImpl* InfiniFrameWindow::ImplBase() noexcept { - return m_impl.get(); +CommonWindowState* GetCommonWindowState(InfiniFrameWindow* window) noexcept { + return &window->m_impl->common; } -const InfiniFrameWindowImpl* InfiniFrameWindow::ImplBase() const noexcept { - return m_impl.get(); +const CommonWindowState* GetCommonWindowState(const InfiniFrameWindow* window) noexcept { + return &window->m_impl->common; } GtkWidget* InfiniFrameWindow::getGtkWindow() { return m_impl->_window; -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/Core/WindowEvents.Gtk.cpp b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/Core/WindowEvents.Gtk.cpp index d0b225afc..ca9b65fec 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/Core/WindowEvents.Gtk.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/Core/WindowEvents.Gtk.cpp @@ -2,17 +2,18 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- #include "Runtime/Platform/Linux/Window.Gtk.Internal.h" +#include "Runtime/Internal/Interop/Types/InfiniFrameWindowInitParams.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- void InfiniFrameWindow::AddCustomSchemeName(const char* scheme) { if (scheme == nullptr) return; - if (m_impl->_customSchemeNames.size() >= InfiniFrameInitParams::MaxCustomSchemeNames) + if (m_impl->common._customSchemeNames.size() >= InfiniFrameWindowInitParams::MaxCustomSchemeNames) return; - for (const auto& existing : m_impl->_customSchemeNames) { + for (const auto& existing : m_impl->common._customSchemeNames) { if (g_ascii_strcasecmp(existing.c_str(), scheme) == 0) return; } - m_impl->_customSchemeNames.emplace_back(scheme); -} \ No newline at end of file + m_impl->common._customSchemeNames.emplace_back(scheme); +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/Core/WindowInitialization.Gtk.cpp b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/Core/WindowInitialization.Gtk.cpp index d9a48c8ba..abe319932 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/Core/WindowInitialization.Gtk.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/Core/WindowInitialization.Gtk.cpp @@ -4,7 +4,7 @@ #include #include -#include "Runtime/Shared/Window/InfiniFrameDialog.h" +#include "Runtime/Internal/Window/InfiniFrameDialog.h" #include "Runtime/Platform/Linux/Window.Gtk.Internal.h" // --------------------------------------------------------------------------------------------------------------------- // Code @@ -24,7 +24,7 @@ gboolean on_webview_context_menu( ); gboolean on_permission_request(WebKitWebView* web_view, WebKitPermissionRequest* request, gpointer user_data); -void InfiniFrameWindow::Impl::InitializeFromParams(const InfiniFrameInitParams* initParams) { +void InfiniFrameWindow::Impl::InitializeFromParams(const InfiniFrameWindowInitParams* initParams) { if (initParams->Title != nullptr) { _windowTitle = initParams->Title; } else { @@ -103,7 +103,7 @@ void InfiniFrameWindow::Impl::InitializeFromParams(const InfiniFrameInitParams* _parent = initParams->ParentInstance; } -void InfiniFrameWindow::Impl::ConfigureInitialWindow(InfiniFrameWindow* window, InfiniFrameInitParams* initParams) { +void InfiniFrameWindow::Impl::ConfigureInitialWindow(InfiniFrameWindow* window, InfiniFrameWindowInitParams* initParams) { _window = gtk_window_new(GTK_WINDOW_TOPLEVEL); _dialog = std::make_unique(); @@ -137,7 +137,7 @@ void InfiniFrameWindow::Impl::ConfigureInitialWindow(InfiniFrameWindow* window, void InfiniFrameWindow::Impl::ApplyInitialWindowState( InfiniFrameWindow* window, - const InfiniFrameInitParams* initParams + const InfiniFrameWindowInitParams* initParams ) { window->SetTitle(const_cast(_windowTitle.c_str())); @@ -231,4 +231,4 @@ void InfiniFrameWindow::Impl::ConnectWebViewSignals(InfiniFrameWindow* window) { g_signal_connect(G_OBJECT(_webview), "context-menu", G_CALLBACK(on_webview_context_menu), window); g_signal_connect(G_OBJECT(_webview), "permission-request", G_CALLBACK(on_permission_request), window); -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/Core/WindowState.Gtk.cpp b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/Core/WindowState.Gtk.cpp index 58d886b72..4c0b0fcab 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/Core/WindowState.Gtk.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/Core/WindowState.Gtk.cpp @@ -5,30 +5,30 @@ #include #include "Runtime/Platform/Linux/Core/GtkCallbackGuard.h" -#include "Runtime/Shared/Utilities/StringCopy.h" +#include "Runtime/Internal/Utilities/StringCopy.h" #include "Runtime/Platform/Linux/Window.Gtk.Internal.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- void InfiniFrameWindow::GetTransparentEnabled(bool* enabled) const { - *enabled = m_impl->_transparentEnabled; + *enabled = m_impl->common._transparentEnabled; } void InfiniFrameWindow::GetContextMenuEnabled(bool* enabled) const { - *enabled = m_impl->_contextMenuEnabled; + *enabled = m_impl->common._contextMenuEnabled; } void InfiniFrameWindow::GetZoomEnabled(bool* enabled) const { - *enabled = m_impl->_zoomEnabled; + *enabled = m_impl->common._zoomEnabled; } void InfiniFrameWindow::GetStatusBarEnabled(bool* enabled) const { - *enabled = m_impl->_statusBarEnabled; + *enabled = m_impl->common._statusBarEnabled; } void InfiniFrameWindow::GetDevToolsEnabled(bool* enabled) const { if (m_impl->_webview == nullptr) { - *enabled = m_impl->_devToolsEnabled; + *enabled = m_impl->common._devToolsEnabled; return; } WebKitSettings* settings = webkit_web_view_get_settings(WEBKIT_WEB_VIEW(m_impl->_webview)); @@ -226,7 +226,7 @@ void InfiniFrameWindow::SendWebMessage(const char* message) { } void InfiniFrameWindow::SetContextMenuEnabled(const bool enabled) { - m_impl->_contextMenuEnabled = enabled; + m_impl->common._contextMenuEnabled = enabled; if (m_impl->_webview == nullptr) return; std::string payload = "{\"enabled\":" + std::string(enabled ? "true" : "false") + "}"; @@ -236,7 +236,7 @@ void InfiniFrameWindow::SetContextMenuEnabled(const bool enabled) { } void InfiniFrameWindow::SetMediaAutoplayEnabled(const bool enabled) { - m_impl->_mediaAutoplayEnabled = enabled; + m_impl->common._mediaAutoplayEnabled = enabled; if (m_impl->_webview == nullptr) return; @@ -246,20 +246,20 @@ void InfiniFrameWindow::SetMediaAutoplayEnabled(const bool enabled) { } void InfiniFrameWindow::SetUserAgent(const char* userAgent) { - m_impl->_userAgent = userAgent != nullptr ? userAgent : ""; + m_impl->common._userAgent = userAgent != nullptr ? userAgent : ""; if (m_impl->_webview == nullptr) return; WebKitSettings* settings = webkit_web_view_get_settings(WEBKIT_WEB_VIEW(m_impl->_webview)); webkit_settings_set_user_agent( settings, - m_impl->_userAgent.empty() ? nullptr : m_impl->_userAgent.c_str() + m_impl->common._userAgent.empty() ? nullptr : m_impl->common._userAgent.c_str() ); webkit_web_view_reload(WEBKIT_WEB_VIEW(m_impl->_webview)); } void InfiniFrameWindow::SetZoomEnabled(const bool enabled) { - m_impl->_zoomEnabled = enabled; + m_impl->common._zoomEnabled = enabled; if (m_impl->_webview == nullptr) return; std::string payload = "{\"enabled\":" + std::string(enabled ? "true" : "false") + "}"; @@ -271,11 +271,11 @@ void InfiniFrameWindow::SetZoomEnabled(const bool enabled) { void InfiniFrameWindow::SetStatusBarEnabled(const bool enabled) { // WebKitGTK has no native status bar concept — this is a WebView2-only feature. // The flag is stored for API consistency but has no visible effect on Linux. - m_impl->_statusBarEnabled = enabled; + m_impl->common._statusBarEnabled = enabled; } void InfiniFrameWindow::SetBrowserShortcutsEnabled(const bool enabled) { - m_impl->_browserShortcutsEnabled = enabled; + m_impl->common._browserShortcutsEnabled = enabled; if (m_impl->_webview == nullptr) return; std::string payload = "{\"enabled\":" + std::string(enabled ? "true" : "false") + "}"; @@ -286,11 +286,11 @@ void InfiniFrameWindow::SetBrowserShortcutsEnabled(const bool enabled) { } void InfiniFrameWindow::SetDevToolsEnabled(const bool enabled) { - m_impl->_devToolsEnabled = enabled; + m_impl->common._devToolsEnabled = enabled; if (m_impl->_webview == nullptr) return; WebKitSettings* settings = webkit_web_view_get_settings(WEBKIT_WEB_VIEW(m_impl->_webview)); - webkit_settings_set_enable_developer_extras(settings, m_impl->_devToolsEnabled || m_impl->_remoteDebuggingPort > 0); + webkit_settings_set_enable_developer_extras(settings, m_impl->common._devToolsEnabled || m_impl->common._remoteDebuggingPort > 0); } void InfiniFrameWindow::SetFullScreen(const bool fullScreen) { @@ -304,7 +304,7 @@ void InfiniFrameWindow::SetFullScreen(const bool fullScreen) { void InfiniFrameWindow::SetIconFile(const char* filename) { gtk_window_set_icon_from_file(GTK_WINDOW(m_impl->_window), filename, nullptr); - m_impl->_iconFileName = filename ? filename : ""; + m_impl->common._iconFileName = filename ? filename : ""; } void InfiniFrameWindow::SetMinimized(const bool minimized) { @@ -382,7 +382,7 @@ void InfiniFrameWindow::SetTopmost(const bool topmost) { void InfiniFrameWindow::SetZoom(const int zoom) { // Software guard: respect EnableZoom(false) to match cross-platform API semantics. - if (!m_impl->_zoomEnabled) + if (!m_impl->common._zoomEnabled) return; // Clamp to valid range (25-500%) to match Windows/macOS behavior. @@ -401,7 +401,7 @@ void InfiniFrameWindow::SetFocused() { } void InfiniFrameWindow::SetTransparentEnabled(const bool enabled) { - m_impl->_transparentEnabled = enabled; + m_impl->common._transparentEnabled = enabled; gtk_window_set_decorated(GTK_WINDOW(m_impl->_window), !enabled); @@ -421,14 +421,14 @@ void InfiniFrameWindow::SetTransparentEnabled(const bool enabled) { } void InfiniFrameWindow::SetBackgroundColor(const uint8_t r, const uint8_t g, const uint8_t b, const uint8_t a) { - m_impl->_backgroundColorR = r; - m_impl->_backgroundColorG = g; - m_impl->_backgroundColorB = b; - m_impl->_backgroundColorA = a; + m_impl->common._backgroundColorR = r; + m_impl->common._backgroundColorG = g; + m_impl->common._backgroundColorB = b; + m_impl->common._backgroundColorA = a; if (m_impl->_webview == nullptr) return; GdkRGBA rgba = {r / 255.0, g / 255.0, b / 255.0, a / 255.0}; webkit_web_view_set_background_color(WEBKIT_WEB_VIEW(m_impl->_webview), &rgba); -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/Dialog.cpp b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/Dialog.cpp index d40cf6dc7..91f196977 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/Dialog.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/Dialog.cpp @@ -4,10 +4,12 @@ #include #include "Runtime/Platform/Linux/Core/UiThread.Gtk.h" -#include "Runtime/Shared/Window/InfiniFrameDialog.h" -#include "Runtime/Shared/Window/InfiniFrameWindow.h" -#include "Runtime/Shared/Operations/DialogOperation.h" -#include "Runtime/Shared/Utilities/StringArrayCopy.h" +#include "Runtime/Internal/Window/InfiniFrameDialog.h" +#include "Runtime/Internal/Interop/Types/InfiniFrameWindow.h" +#include "Runtime/Internal/Operations/DialogOperation.h" +#include "Runtime/Internal/Utilities/StringArrayCopy.h" + +struct InfiniFrameDialog::Impl {}; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -139,7 +141,7 @@ const char** ShowDialog( return arr; } -InfiniFrameDialog::InfiniFrameDialog() {} +InfiniFrameDialog::InfiniFrameDialog() : m_impl(std::make_unique()) {} InfiniFrameDialog::~InfiniFrameDialog() {} @@ -544,4 +546,4 @@ void InfiniFrameWindow::BeginShowSaveFile( ) { BeginAsyncFileDialog( this, SaveFile, id, title, path, false, filters, filterCount, defaultFileName, completion, context); -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/Menu.Gtk.cpp b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/Menu.Gtk.cpp index bd03ba32c..3f3e15d51 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/Menu.Gtk.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/Menu.Gtk.cpp @@ -167,7 +167,7 @@ void InfiniFrameWindow::ApplyInitMenuBar(const char* menuBarJson) { if (menuBarJson == nullptr || menuBarJson[0] == '\0') return; - auto* impl = static_cast(ImplBase()); + auto* impl = m_impl.get(); if (impl->_menuBar != nullptr) { DestroyMenuActivateData(impl); @@ -212,7 +212,7 @@ void InfiniFrameWindow::SetMenuBarJson(const char* menuBarJson) { } void InfiniFrameWindow::SetMenuItemEnabledById(const char* menuItemId, const bool enabled) { - auto* impl = static_cast(ImplBase()); + auto* impl = m_impl.get(); if (impl->_menuBar == nullptr) return; @@ -224,7 +224,7 @@ void InfiniFrameWindow::SetMenuItemEnabledById(const char* menuItemId, const boo } void InfiniFrameWindow::SetMenuItemVisibleById(const char* menuItemId, const bool visible) { - auto* impl = static_cast(ImplBase()); + auto* impl = m_impl.get(); if (impl->_menuBar == nullptr) return; @@ -240,9 +240,9 @@ void InfiniFrameWindow::SetMenuItemVisibleById(const char* menuItemId, const boo } void InfiniFrameWindow::ClickMenuItemById(const char* menuItemId) { - if (m_impl->_webMessageReceivedCallback == nullptr) + if (m_impl->common._webMessageReceivedCallback == nullptr) return; std::string message = std::string("menu:") + menuItemId; SendWebMessage(message.c_str()); -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/Notifications.LibNotify.cpp b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/Notifications.LibNotify.cpp index cd6363303..c3ac15fab 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/Notifications.LibNotify.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/Notifications.LibNotify.cpp @@ -4,7 +4,7 @@ #include #include "Runtime/Platform/Linux/Window.Gtk.Internal.h" -#include "Api/Utilities/ExportStringHelpers.h" +#include "Runtime/Internal/Interop/Exports/ExportStringHelpers.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -72,4 +72,4 @@ void InfiniFrameWindow::CancelNotification(const uint64_t operationId, bool* can (void)operationId; if (canceled) *canceled = false; -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/WebKit/WebKitCustomSchemes.Gtk.cpp b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/WebKit/WebKitCustomSchemes.Gtk.cpp index 1caea7213..8f4161524 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/WebKit/WebKitCustomSchemes.Gtk.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/WebKit/WebKitCustomSchemes.Gtk.cpp @@ -8,7 +8,7 @@ #include "Runtime/Platform/Linux/Window.Gtk.Internal.h" #include "Runtime/Platform/Linux/WebKit/WebKit.Gtk.Internal.h" -#include "Runtime/Shared/WebView/CustomSchemeResponse.h" +#include "Runtime/Internal/WebView/CustomSchemeResponse.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -93,4 +93,4 @@ void InfiniFrameWindow::Impl::AddCustomSchemeHandlers() { reinterpret_cast(_customSchemeCallback), nullptr ); } -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/WebKit/WebKitHost.Gtk.cpp b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/WebKit/WebKitHost.Gtk.cpp index e8ae4198a..cffa45b83 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/WebKit/WebKitHost.Gtk.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/WebKit/WebKitHost.Gtk.cpp @@ -80,7 +80,7 @@ void InfiniFrameWindow::Show(const bool isAlreadyShown) { // window.webkit.messageHandlers.infiniFrameInterop.postMessage(). m_impl->_webMessageSignalHandlerId = g_signal_connect( contentManager, "script-message-received::infiniFrameInterop", G_CALLBACK(gtk_webkit::HandleWebMessage), - reinterpret_cast(m_impl->_webMessageReceivedCallback) + reinterpret_cast(m_impl->common._webMessageReceivedCallback) ); webkit_user_content_manager_register_script_message_handler(contentManager, "infiniFrameInterop"); @@ -96,10 +96,10 @@ void InfiniFrameWindow::Show(const bool isAlreadyShown) { // Navigate to the initial content. Show an error dialog if neither URL // nor raw string was provided. - if (!m_impl->_startUrl.empty()) { - NavigateToUrl(const_cast(m_impl->_startUrl.c_str())); - } else if (!m_impl->_startString.empty()) { - NavigateToString(const_cast(m_impl->_startString.c_str())); + if (!m_impl->common._startUrl.empty()) { + NavigateToUrl(const_cast(m_impl->common._startUrl.c_str())); + } else if (!m_impl->common._startString.empty()) { + NavigateToString(const_cast(m_impl->common._startString.c_str())); } else { GtkWidget* dialog = gtk_message_dialog_new( nullptr, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/WebKit/WebKitMessaging.Gtk.cpp b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/WebKit/WebKitMessaging.Gtk.cpp index eeddb076d..c0c7a1e99 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/WebKit/WebKitMessaging.Gtk.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/WebKit/WebKitMessaging.Gtk.cpp @@ -4,8 +4,8 @@ #include #include "Runtime/Platform/Linux/Core/GtkCallbackGuard.h" -#include "Runtime/Shared/Types/Basic.h" -#include "Runtime/Shared/Types/Callbacks.h" +#include "Runtime/Internal/Interop/Types/Basic.h" +#include "Runtime/Internal/Interop/Types/Callbacks.h" #include "Runtime/Platform/Linux/WebKit/WebKit.Gtk.Internal.h" // --------------------------------------------------------------------------------------------------------------------- // Code @@ -73,4 +73,4 @@ namespace gtk_webkit { } }); } -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/Window.Gtk.Internal.h b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/Window.Gtk.Internal.h index 9b25102d5..aceb039d1 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/Window.Gtk.Internal.h +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Linux/Window.Gtk.Internal.h @@ -11,18 +11,17 @@ #include #include -#include "Runtime/Shared/Window/InfiniFrameWindow.h" -#include "Runtime/Shared/Window/InfiniFrameWindowImpl.h" +#include "Runtime/Internal/Interop/Types/InfiniFrameWindow.h" +#include "Runtime/Internal/Window/CommonWindowState.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- -struct InfiniFrameWindow::Impl : InfiniFrameWindowImpl { +struct InfiniFrameWindow::Impl { + CommonWindowState common; GtkWidget* _window = nullptr; GtkWidget* _webview = nullptr; WebKitWebContext* _webContext = nullptr; gulong _webMessageSignalHandlerId = 0; - int _remoteDebuggingPort = 0; - std::string _temporaryFilesPath; GtkWidget* _menuBar = nullptr; @@ -65,9 +64,9 @@ struct InfiniFrameWindow::Impl : InfiniFrameWindowImpl { void configure_webkit_remote_debugging() const; void set_webkit_customsettings(WebKitSettings* settings); void AddCustomSchemeHandlers(); - void InitializeFromParams(const InfiniFrameInitParams* initParams); - void ConfigureInitialWindow(InfiniFrameWindow* window, InfiniFrameInitParams* initParams); - void ApplyInitialWindowState(InfiniFrameWindow* window, const InfiniFrameInitParams* initParams); + void InitializeFromParams(const InfiniFrameWindowInitParams* initParams); + void ConfigureInitialWindow(InfiniFrameWindow* window, InfiniFrameWindowInitParams* initParams); + void ApplyInitialWindowState(InfiniFrameWindow* window, const InfiniFrameWindowInitParams* initParams); void ConnectWindowSignals(InfiniFrameWindow* window); void ConnectWebViewSignals(InfiniFrameWindow* window); -}; \ No newline at end of file +}; diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Core/UiDispatcher.Cocoa.mm b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Core/UiDispatcher.Cocoa.mm index e8db823dc..dd83f168d 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Core/UiDispatcher.Cocoa.mm +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Core/UiDispatcher.Cocoa.mm @@ -2,7 +2,7 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- #include "../Window.Cocoa.Internal.h" -#include "Runtime/Shared/Operations/NativeOperation.h" +#include "Runtime/Internal/Operations/NativeOperation.h" #include #include diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Core/WindowCore.Cocoa.mm b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Core/WindowCore.Cocoa.mm index 79569750a..773f19f7b 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Core/WindowCore.Cocoa.mm +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Core/WindowCore.Cocoa.mm @@ -21,8 +21,10 @@ #include "../Delegates/NavigationDelegate.h" #include "../Delegates/UiDelegate.h" #include "../Delegates/UrlSchemeHandler.h" -#include "Runtime/Shared/Window/InfiniFrameDialog.h" -#include "Runtime/Shared/Window/InfiniFrameWindow.h" +#include "Runtime/Internal/Window/InfiniFrameDialog.h" +#include "Runtime/Internal/Interop/Types/InfiniFrameWindow.h" +#include "Runtime/Internal/Interop/Types/InfiniFrameWindowInitParams.h" +#include "Runtime/Internal/Application/InfiniFrameApplication.h" #include "../NSWindowBorderless.h" #include "../MacDiagnostics.h" #include "../Window.Cocoa.Internal.h" @@ -81,7 +83,7 @@ void ReleaseWebKitObjectsSafely(WKWebView* webview, WKWebViewConfiguration* conf [configuration release]; } -std::string HostCompatibilityKey(const InfiniFrameInitParams* p) { +std::string HostCompatibilityKey(const InfiniFrameWindowInitParams* p) { // Every value below is consumed while constructing/configuring WKWebView. Exact JSON and // scheme ordering are retained rather than trying to normalize arbitrary WebKit preferences. std::string key = p->Chromeless ? "chromeless=1;" : "chromeless=0;"; @@ -213,8 +215,9 @@ size_t PooledMacHostCountForTesting() { }); } -InfiniFrameWindow::InfiniFrameWindow(InfiniFrameInitParams* initParams) : m_impl(std::make_unique()) +InfiniFrameWindow::InfiniFrameWindow(InfiniFrameWindowInitParams* initParams) : m_impl(std::make_unique()) { + _application = initParams->ApplicationInstance; infiniframe::macos::LogLifecycle("window-construct-begin", this); const bool traceTimings = std::getenv("INFINIFRAME_MACOS_TRACE_TIMINGS") != nullptr; const auto constructionStartedAt = std::chrono::steady_clock::now(); @@ -223,57 +226,57 @@ size_t PooledMacHostCountForTesting() { try { DispatchToMainSync(^{ @autoreleasepool { - this->m_impl->_windowTitle = params->Title ? params->Title : ""; + this->m_impl->common._windowTitle = params->Title ? params->Title : ""; if (params->StartUrl != nullptr) - this->m_impl->_startUrl = params->StartUrl; + this->m_impl->common._startUrl = params->StartUrl; if (params->StartString != nullptr) - this->m_impl->_startString = params->StartString; + this->m_impl->common._startString = params->StartString; if (params->TemporaryFilesPath != nullptr) this->m_impl->_temporaryFilesPath = params->TemporaryFilesPath; - this->m_impl->_ignoreCertificateErrorsEnabled = params->IgnoreCertificateErrorsEnabled; - this->m_impl->_contextMenuEnabled = params->ContextMenuEnabled; - this->m_impl->_zoomEnabled = params->ZoomEnabled; + this->m_impl->common._ignoreCertificateErrorsEnabled = params->IgnoreCertificateErrorsEnabled; + this->m_impl->common._contextMenuEnabled = params->ContextMenuEnabled; + this->m_impl->common._zoomEnabled = params->ZoomEnabled; this->m_impl->_zoom = params->Zoom; - this->m_impl->_devToolsEnabled = params->DevToolsEnabled; - this->m_impl->_webInspectorEnabled = params->WebInspectorEnabled; - this->m_impl->_grantBrowserPermissions = params->GrantBrowserPermissions; - this->m_impl->_mediaAutoplayEnabled = params->MediaAutoplayEnabled; - this->m_impl->_fileSystemAccessEnabled = params->FileSystemAccessEnabled; - this->m_impl->_webSecurityEnabled = params->WebSecurityEnabled; - this->m_impl->_javascriptClipboardAccessEnabled = params->JavascriptClipboardAccessEnabled; - this->m_impl->_mediaStreamEnabled = params->MediaStreamEnabled; - this->m_impl->_smoothScrollingEnabled = params->SmoothScrollingEnabled; - this->m_impl->_statusBarEnabled = params->StatusBarEnabled; - this->m_impl->_browserShortcutsEnabled = params->BrowserShortcutsEnabled; - this->m_impl->_remoteDebuggingPort = params->RemoteDebuggingPort; + this->m_impl->common._devToolsEnabled = params->DevToolsEnabled; + this->m_impl->common._webInspectorEnabled = params->WebInspectorEnabled; + this->m_impl->common._grantBrowserPermissions = params->GrantBrowserPermissions; + this->m_impl->common._mediaAutoplayEnabled = params->MediaAutoplayEnabled; + this->m_impl->common._fileSystemAccessEnabled = params->FileSystemAccessEnabled; + this->m_impl->common._webSecurityEnabled = params->WebSecurityEnabled; + this->m_impl->common._javascriptClipboardAccessEnabled = params->JavascriptClipboardAccessEnabled; + this->m_impl->common._mediaStreamEnabled = params->MediaStreamEnabled; + this->m_impl->common._smoothScrollingEnabled = params->SmoothScrollingEnabled; + this->m_impl->common._statusBarEnabled = params->StatusBarEnabled; + this->m_impl->common._browserShortcutsEnabled = params->BrowserShortcutsEnabled; + this->m_impl->common._remoteDebuggingPort = params->RemoteDebuggingPort; if (params->DefaultNotificationIcon != nullptr) - this->m_impl->_defaultNotificationIcon = params->DefaultNotificationIcon; - - this->m_impl->_webMessageReceivedCallback = params->WebMessageReceivedHandler; - this->m_impl->_resizedCallback = params->ResizedHandler; - this->m_impl->_movedCallback = params->MovedHandler; - this->m_impl->_closingCallback = params->ClosingHandler; - this->m_impl->_closedCallback = params->ClosedHandler; - this->m_impl->_focusInCallback = params->FocusInHandler; - this->m_impl->_focusOutCallback = params->FocusOutHandler; - this->m_impl->_maximizedCallback = params->MaximizedHandler; - this->m_impl->_minimizedCallback = params->MinimizedHandler; - this->m_impl->_restoredCallback = params->RestoredHandler; - this->m_impl->_debugEventCallback = params->DebugEventHandler; - this->m_impl->_customSchemeCallback = params->CustomSchemeHandler; - this->m_impl->_navigationStartingCallback = params->NavigationStartingHandler; + this->m_impl->common._defaultNotificationIcon = params->DefaultNotificationIcon; + + this->m_impl->common._webMessageReceivedCallback = params->WebMessageReceivedHandler; + this->m_impl->common._resizedCallback = params->ResizedHandler; + this->m_impl->common._movedCallback = params->MovedHandler; + this->m_impl->common._closingCallback = params->ClosingHandler; + this->m_impl->common._closedCallback = params->ClosedHandler; + this->m_impl->common._focusInCallback = params->FocusInHandler; + this->m_impl->common._focusOutCallback = params->FocusOutHandler; + this->m_impl->common._maximizedCallback = params->MaximizedHandler; + this->m_impl->common._minimizedCallback = params->MinimizedHandler; + this->m_impl->common._restoredCallback = params->RestoredHandler; + this->m_impl->common._debugEventCallback = params->DebugEventHandler; + this->m_impl->common._customSchemeCallback = params->CustomSchemeHandler; + this->m_impl->common._navigationStartingCallback = params->NavigationStartingHandler; for (int i = 0; i < 16; ++i) { if (params->CustomSchemeNames[i] != nullptr) - this->m_impl->_customSchemeNames.emplace_back(params->CustomSchemeNames[i]); + this->m_impl->common._customSchemeNames.emplace_back(params->CustomSchemeNames[i]); } - this->m_impl->_parent = params->ParentInstance; + this->m_impl->common._parent = params->ParentInstance; if (params->UseOsDefaultSize) { @@ -328,15 +331,15 @@ size_t PooledMacHostCountForTesting() { // teardown and causes a second release at the next autorelease-pool drain. [this->m_impl->_window setReleasedWhenClosed:NO]; - this->m_impl->_transparentEnabled = params->Transparent; - this->m_impl->_backgroundColorR = params->BackgroundColorR; - this->m_impl->_backgroundColorG = params->BackgroundColorG; - this->m_impl->_backgroundColorB = params->BackgroundColorB; - this->m_impl->_backgroundColorA = params->BackgroundColorA; + this->m_impl->common._transparentEnabled = params->Transparent; + this->m_impl->common._backgroundColorR = params->BackgroundColorR; + this->m_impl->common._backgroundColorG = params->BackgroundColorG; + this->m_impl->common._backgroundColorB = params->BackgroundColorB; + this->m_impl->common._backgroundColorA = params->BackgroundColorA; - if (this->m_impl->_parent != nullptr && this->m_impl->_parent->m_impl != nullptr) + if (this->m_impl->common._parent != nullptr && this->m_impl->common._parent->m_impl != nullptr) { - auto* parentImpl = static_cast(this->m_impl->_parent->m_impl.get()); + auto* parentImpl = static_cast(this->m_impl->common._parent->m_impl.get()); this->m_impl->_nativeParentWindow = parentImpl->_window; if (this->m_impl->_nativeParentWindow != nil && this->m_impl->_nativeParentWindow != this->m_impl->_window) { @@ -364,7 +367,7 @@ size_t PooledMacHostCountForTesting() { this->m_impl->_windowDelegate->infiniFrame = this; this->m_impl->_window.delegate = this->m_impl->_windowDelegate; - this->SetTitle(const_cast(this->m_impl->_windowTitle.c_str())); + this->SetTitle(const_cast(this->m_impl->common._windowTitle.c_str())); if (params->WindowIconFile != nullptr && params->WindowIconFile[0] != '\0') this->SetIconFile(params->WindowIconFile); @@ -387,11 +390,11 @@ size_t PooledMacHostCountForTesting() { // A pooled host must never carry persistent browser data into a later logical session. // The store is also cleared before the host is leased again (see CloseWebView). this->m_impl->_webviewConfiguration.websiteDataStore = [WKWebsiteDataStore nonPersistentDataStore]; - this->SetMediaAutoplayEnabled(this->m_impl->_mediaAutoplayEnabled); + this->SetMediaAutoplayEnabled(this->m_impl->common._mediaAutoplayEnabled); - for (const auto & scheme : this->m_impl->_customSchemeNames) + for (const auto & scheme : this->m_impl->common._customSchemeNames) { - this->m_impl->AddCustomScheme(scheme.c_str(), this->m_impl->_customSchemeCallback); + this->m_impl->AddCustomScheme(scheme.c_str(), this->m_impl->common._customSchemeCallback); } webViewStartedAt = std::chrono::steady_clock::now(); @@ -479,11 +482,11 @@ size_t PooledMacHostCountForTesting() { // session-specific state; AttachWebView reinstalls the interop handler and starts // the new document. this->m_impl->_chromeless = params->Chromeless; - this->m_impl->_transparentEnabled = params->Transparent; + this->m_impl->common._transparentEnabled = params->Transparent; this->m_impl->_windowDelegate->infiniFrame = this; this->m_impl->_window.delegate = this->m_impl->_windowDelegate; - if (this->m_impl->_parent != nullptr && this->m_impl->_parent->m_impl != nullptr) { - auto* parentImpl = static_cast(this->m_impl->_parent->m_impl.get()); + if (this->m_impl->common._parent != nullptr && this->m_impl->common._parent->m_impl != nullptr) { + auto* parentImpl = static_cast(this->m_impl->common._parent->m_impl.get()); this->m_impl->_nativeParentWindow = parentImpl->_window; if (this->m_impl->_nativeParentWindow != nil) { [this->m_impl->_nativeParentWindow addChildWindow:this->m_impl->_window ordered:NSWindowAbove]; @@ -493,7 +496,7 @@ size_t PooledMacHostCountForTesting() { usingBlock:^(NSNotification*) { [childWindow orderOut:nil]; }]; } } - this->SetTitle(const_cast(this->m_impl->_windowTitle.c_str())); + this->SetTitle(const_cast(this->m_impl->common._windowTitle.c_str())); this->SetTopmost(params->Topmost); this->SetPosition(params->Left, params->Top); this->SetMinSize(params->MinWidth, params->MinHeight); @@ -501,7 +504,7 @@ size_t PooledMacHostCountForTesting() { this->SetSize(params->Width, params->Height); this->SetResizable(params->Resizable); for (UrlSchemeHandler* handler : this->m_impl->_urlSchemeHandlers) - handler->requestHandler = this->m_impl->_customSchemeCallback; + handler->requestHandler = this->m_impl->common._customSchemeCallback; this->AttachWebView(); if (params->CenterOnInitialize) this->Center(); this->SetMinimized(params->Minimized); @@ -509,7 +512,7 @@ size_t PooledMacHostCountForTesting() { this->SetFullScreen(params->FullScreen); } - this->m_impl->_dialog = std::make_unique(); + this->m_impl->common._dialog = std::make_unique(); if (params->MenuBarJson != nullptr && params->MenuBarJson[0] != '\0') this->ApplyInitMenuBar(params->MenuBarJson); @@ -564,7 +567,7 @@ size_t PooledMacHostCountForTesting() { } [m_impl->_windowDelegate release]; m_impl->_windowDelegate = nil; - m_impl->_dialog.reset(); + m_impl->common._dialog.reset(); }); throw; } @@ -586,11 +589,15 @@ size_t PooledMacHostCountForTesting() { ); } infiniframe::macos::LogLifecycle("window-construct-complete", this); + if (_application != nullptr) + _application->TrackWindow(this); } InfiniFrameWindow::~InfiniFrameWindow() { infiniframe::macos::LogLifecycle("window-destruct-begin", this); + if (_application != nullptr) + _application->UntrackWindow(this); // SafeHandle finalization and managed disposal can release the native window from a // non-AppKit thread. All Cocoa/WebKit teardown must therefore occur on the main queue. DispatchToMainSync(^{ @@ -603,7 +610,7 @@ size_t PooledMacHostCountForTesting() { CloseWebView(); if (m_impl->_window != nil) m_impl->ReturnPooledMacHost(); - m_impl->_dialog.reset(); + m_impl->common._dialog.reset(); m_impl->_windowClosed.store(true, std::memory_order_release); m_impl->_windowClosedCondition.notify_all(); return; @@ -671,7 +678,7 @@ size_t PooledMacHostCountForTesting() { // InfiniFrameDialog owns NSImage instances and must be destroyed on AppKit's thread. infiniframe::macos::LogLifecycle("window-destruct-dialog", this); - m_impl->_dialog.reset(); + m_impl->common._dialog.reset(); m_impl->_windowClosed.store(true, std::memory_order_release); m_impl->_windowClosedCondition.notify_all(); @@ -680,8 +687,13 @@ size_t PooledMacHostCountForTesting() { infiniframe::macos::LogLifecycle("window-destruct-complete", this); } -InfiniFrameWindowImpl* InfiniFrameWindow::ImplBase() noexcept { return m_impl.get(); } -const InfiniFrameWindowImpl* InfiniFrameWindow::ImplBase() const noexcept { return m_impl.get(); } +CommonWindowState* GetCommonWindowState(InfiniFrameWindow* window) noexcept { + return &window->m_impl->common; +} + +const CommonWindowState* GetCommonWindowState(const InfiniFrameWindow* window) noexcept { + return &window->m_impl->common; +} NSWindow* InfiniFrameWindow::getNSWindow() { return m_impl->_window; diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Core/WindowEvents.Cocoa.mm b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Core/WindowEvents.Cocoa.mm index d4ba7ddd7..dc345698c 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Core/WindowEvents.Cocoa.mm +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Core/WindowEvents.Cocoa.mm @@ -3,6 +3,7 @@ // --------------------------------------------------------------------------------------------------------------------- #include "../Window.Cocoa.Internal.h" +#include "Runtime/Internal/Interop/Types/InfiniFrameWindowInitParams.h" // --------------------------------------------------------------------------------------------------------------------- // Code @@ -12,11 +13,11 @@ { if (scheme == nullptr) return; - if (m_impl->_customSchemeNames.size() >= InfiniFrameInitParams::MaxCustomSchemeNames) + if (m_impl->common._customSchemeNames.size() >= InfiniFrameWindowInitParams::MaxCustomSchemeNames) return; - for (const auto& existing : m_impl->_customSchemeNames) { + for (const auto& existing : m_impl->common._customSchemeNames) { if (strcasecmp(existing.c_str(), scheme) == 0) return; } - m_impl->_customSchemeNames.emplace_back(scheme); + m_impl->common._customSchemeNames.emplace_back(scheme); } diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Core/WindowLifecycle.Cocoa.mm b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Core/WindowLifecycle.Cocoa.mm index 618cf9ade..712bc33af 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Core/WindowLifecycle.Cocoa.mm +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Core/WindowLifecycle.Cocoa.mm @@ -197,18 +197,18 @@ static void DispatchToMainSync(void (^block)()) { m_impl->_webviewReady = false; m_impl->_pendingWebMessages.clear(); - m_impl->_closingCallback = nullptr; - m_impl->_closedCallback = nullptr; - m_impl->_focusInCallback = nullptr; - m_impl->_focusOutCallback = nullptr; - m_impl->_movedCallback = nullptr; - m_impl->_resizedCallback = nullptr; - m_impl->_maximizedCallback = nullptr; - m_impl->_restoredCallback = nullptr; - m_impl->_minimizedCallback = nullptr; - m_impl->_debugEventCallback = nullptr; - m_impl->_customSchemeCallback = nullptr; - m_impl->_webMessageReceivedCallback = nullptr; + m_impl->common._closingCallback = nullptr; + m_impl->common._closedCallback = nullptr; + m_impl->common._focusInCallback = nullptr; + m_impl->common._focusOutCallback = nullptr; + m_impl->common._movedCallback = nullptr; + m_impl->common._resizedCallback = nullptr; + m_impl->common._maximizedCallback = nullptr; + m_impl->common._restoredCallback = nullptr; + m_impl->common._minimizedCallback = nullptr; + m_impl->common._debugEventCallback = nullptr; + m_impl->common._customSchemeCallback = nullptr; + m_impl->common._webMessageReceivedCallback = nullptr; if (m_impl->_uiDelegate != nil) m_impl->_uiDelegate->webMessageReceivedCallback = nullptr; diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Core/WindowState.Cocoa.mm b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Core/WindowState.Cocoa.mm index 71614122b..76a4dad90 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Core/WindowState.Cocoa.mm +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Core/WindowState.Cocoa.mm @@ -5,7 +5,7 @@ #include "../CocoaCoordinates.h" #include "../WebKit/InfiniFrameWebView.h" -#include "Runtime/Shared/Utilities/StringCopy.h" +#include "Runtime/Internal/Utilities/StringCopy.h" #include // --------------------------------------------------------------------------------------------------------------------- // Code @@ -36,37 +36,37 @@ [m_impl->_webviewConfiguration methodForSelector: selector]); const NSUInteger mediaTypesMask = - m_impl->_mediaAutoplayEnabled ? 0u : NSUIntegerMax; + m_impl->common._mediaAutoplayEnabled ? 0u : NSUIntegerMax; setter(m_impl->_webviewConfiguration, selector, mediaTypesMask); } void InfiniFrameWindow::GetTransparentEnabled(bool* enabled) const { - *enabled = m_impl->_transparentEnabled; + *enabled = m_impl->common._transparentEnabled; } void InfiniFrameWindow::GetContextMenuEnabled(bool* enabled) const { // Returns cached state; actual context menu is controlled via InfiniFrameWebView override. - *enabled = m_impl->_contextMenuEnabled; + *enabled = m_impl->common._contextMenuEnabled; } void InfiniFrameWindow::GetZoomEnabled(bool* enabled) const { // Returns cached state; actual zoom is controlled via InfiniFrameWebView override. - *enabled = m_impl->_zoomEnabled; + *enabled = m_impl->common._zoomEnabled; } void InfiniFrameWindow::GetStatusBarEnabled(bool* enabled) const { // WKWebView has no native status bar — this flag is stored for API consistency. - *enabled = m_impl->_statusBarEnabled; + *enabled = m_impl->common._statusBarEnabled; } void InfiniFrameWindow::GetDevToolsEnabled(bool* enabled) const { - *enabled = m_impl->_devToolsEnabled; + *enabled = m_impl->common._devToolsEnabled; } void InfiniFrameWindow::GetFullScreen(bool* fullScreen) const @@ -149,7 +149,7 @@ const char* InfiniFrameWindow::GetTitle() const { - return AllocateStringCopy(m_impl->_windowTitle); + return AllocateStringCopy(m_impl->common._windowTitle); } const char* InfiniFrameWindow::GetCurrentUrl() const @@ -280,13 +280,13 @@ void InfiniFrameWindow::SetDevToolsEnabled(bool enabled) { - m_impl->_devToolsEnabled = enabled; + m_impl->common._devToolsEnabled = enabled; m_impl->SetPreference(@"developerExtrasEnabled", enabled ? @YES : @NO); } void InfiniFrameWindow::SetTransparentEnabled(bool enabled) { - m_impl->_transparentEnabled = enabled; + m_impl->common._transparentEnabled = enabled; [m_impl->_window setOpaque:enabled ? NO : YES]; [m_impl->_window setBackgroundColor:enabled ? [NSColor clearColor] : [NSColor windowBackgroundColor]]; @@ -298,7 +298,7 @@ void InfiniFrameWindow::SetContextMenuEnabled(bool enabled) { - m_impl->_contextMenuEnabled = enabled; + m_impl->common._contextMenuEnabled = enabled; if (m_impl->_webview != nil) { InfiniFrameWebView* webView = (InfiniFrameWebView*)m_impl->_webview; [webView setInfiniFrameContextMenuEnabled:enabled ? YES : NO]; @@ -307,7 +307,7 @@ void InfiniFrameWindow::SetMediaAutoplayEnabled(bool enabled) { - m_impl->_mediaAutoplayEnabled = enabled; + m_impl->common._mediaAutoplayEnabled = enabled; ApplyMediaAutoplayConfiguration(); if (m_impl->_webview != nil) @@ -324,7 +324,7 @@ void InfiniFrameWindow::SetZoomEnabled(bool enabled) { - m_impl->_zoomEnabled = enabled; + m_impl->common._zoomEnabled = enabled; if (m_impl->_webview != nil) { InfiniFrameWebView* webView = (InfiniFrameWebView*)m_impl->_webview; [webView setInfiniFrameZoomEnabled:enabled ? YES : NO]; @@ -335,12 +335,12 @@ { // WKWebView has no native status bar concept — this is a WebView2-only feature. // The flag is stored for API consistency but has no visible effect on macOS. - m_impl->_statusBarEnabled = enabled; + m_impl->common._statusBarEnabled = enabled; } void InfiniFrameWindow::SetBrowserShortcutsEnabled(bool enabled) { - m_impl->_browserShortcutsEnabled = enabled; + m_impl->common._browserShortcutsEnabled = enabled; if (m_impl->_webview == nil) return; @autoreleasepool { @@ -368,7 +368,7 @@ [[m_impl->_window standardWindowButton: NSWindowDocumentIconButton] setImage: icon]; [icon release]; - m_impl->_iconFileName = filename ? filename : ""; + m_impl->common._iconFileName = filename ? filename : ""; } void InfiniFrameWindow::SetFullScreen(bool fullScreen) @@ -476,7 +476,7 @@ void InfiniFrameWindow::SetTitle(const char* title) { - m_impl->_windowTitle = title ? title : ""; + m_impl->common._windowTitle = title ? title : ""; [m_impl->_window setTitle:RequireUtf8String(title, "title")]; } @@ -489,7 +489,7 @@ void InfiniFrameWindow::SetZoom(int zoom) { // Software guard: respect EnableZoom(false) to match cross-platform API semantics. - if (!m_impl->_zoomEnabled) + if (!m_impl->common._zoomEnabled) return; // Clamp to valid range (25-500%) to match Windows/Linux behavior. @@ -520,10 +520,10 @@ void InfiniFrameWindow::SetBackgroundColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { - m_impl->_backgroundColorR = r; - m_impl->_backgroundColorG = g; - m_impl->_backgroundColorB = b; - m_impl->_backgroundColorA = a; + m_impl->common._backgroundColorR = r; + m_impl->common._backgroundColorG = g; + m_impl->common._backgroundColorB = b; + m_impl->common._backgroundColorA = a; if (m_impl->_webview == nil) return; diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Delegates/NavigationDelegate.h b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Delegates/NavigationDelegate.h index d5a208d7a..95bce4890 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Delegates/NavigationDelegate.h +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Delegates/NavigationDelegate.h @@ -7,7 +7,7 @@ * @file NavigationDelegate.h * @brief WKNavigationDelegate that handles TLS certificate validation for the embedded WebView */ -#include "Runtime/Shared/Window/InfiniFrame.h" +#include "Runtime/Internal/Interop/Types/InfiniFrameWindow.h" // --------------------------------------------------------------------------------------------------------------------- // Code @@ -17,7 +17,7 @@ * @brief Navigation delegate conforming to WKNavigationDelegate. * * Intercepts authentication challenges to optionally suppress TLS certificate - * errors when InfiniFrameInitParams::IgnoreCertificateErrors is set + * errors when InfiniFrameWindowInitParams::IgnoreCertificateErrors is set */ @ interface NavigationDelegate: diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Delegates/UiDelegate.h b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Delegates/UiDelegate.h index 61caf9176..403075155 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Delegates/UiDelegate.h +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Delegates/UiDelegate.h @@ -7,7 +7,7 @@ * @file UiDelegate.h * @brief WKUIDelegate and WKScriptMessageHandler that routes JavaScript messages to the .NET layer */ -#include "Runtime/Shared/Window/InfiniFrame.h" +#include "Runtime/Internal/Interop/Types/InfiniFrameWindow.h" // --------------------------------------------------------------------------------------------------------------------- // Code diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Delegates/UrlSchemeHandler.h b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Delegates/UrlSchemeHandler.h index beb8f6843..55cf99785 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Delegates/UrlSchemeHandler.h +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Delegates/UrlSchemeHandler.h @@ -2,7 +2,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -#include "Runtime/Shared/Window/InfiniFrame.h" +#include "Runtime/Internal/Interop/Types/InfiniFrameWindow.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Delegates/UrlSchemeHandler.mm b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Delegates/UrlSchemeHandler.mm index 19a437c2f..970d35037 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Delegates/UrlSchemeHandler.mm +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Delegates/UrlSchemeHandler.mm @@ -4,7 +4,7 @@ #import "UrlSchemeHandler.h" #include "../MacDiagnostics.h" -#include "Runtime/Shared/WebView/CustomSchemeResponse.h" +#include "Runtime/Internal/WebView/CustomSchemeResponse.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Delegates/WindowDelegate.h b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Delegates/WindowDelegate.h index 0767d5728..1994e387b 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Delegates/WindowDelegate.h +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Delegates/WindowDelegate.h @@ -7,7 +7,7 @@ * @file WindowDelegate.h * @brief NSWindow delegate that forwards window lifecycle events to InfiniFrameWindow callbacks */ -#include "Runtime/Shared/Window/InfiniFrame.h" +#include "Runtime/Internal/Interop/Types/InfiniFrameWindow.h" // --------------------------------------------------------------------------------------------------------------------- // Code diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Dialog.mm b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Dialog.mm index db7b3bc05..f8d6a03c5 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Dialog.mm +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Dialog.mm @@ -1,10 +1,22 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -#import "Runtime/Shared/Window/InfiniFrameDialog.h" -#include "Runtime/Shared/Window/InfiniFrameWindow.h" -#include "Runtime/Shared/Operations/DialogOperation.h" -#include "Runtime/Shared/Utilities/StringArrayCopy.h" +#import "Runtime/Internal/Window/InfiniFrameDialog.h" +#include "Runtime/Internal/Interop/Types/InfiniFrameWindow.h" +#include "Runtime/Internal/Operations/DialogOperation.h" +#include "Runtime/Internal/Utilities/StringArrayCopy.h" + +struct InfiniFrameDialog::Impl { + NSImage* errorIcon = nil; + NSImage* infoIcon = nil; + NSImage* questionIcon = nil; + NSImage* warningIcon = nil; +}; + +#define _errorIcon m_impl->errorIcon +#define _infoIcon m_impl->infoIcon +#define _questionIcon m_impl->questionIcon +#define _warningIcon m_impl->warningIcon #if defined(VSTGUI_USE_OBJC_UTTYPE) #import @@ -34,6 +46,7 @@ } InfiniFrameDialog::InfiniFrameDialog() { + m_impl = std::make_unique(); _errorIcon = getIcon(errorBase64); _infoIcon = getIcon(infoBase64); _questionIcon = getIcon(questionBase64); diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Menu.Cocoa.mm b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Menu.Cocoa.mm index b4b521926..466bdca85 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Menu.Cocoa.mm +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Menu.Cocoa.mm @@ -231,7 +231,7 @@ void BuildMenuFromJson( } void InfiniFrameWindow::ClickMenuItemById(const char* menuItemId) { - if (m_impl->_webMessageReceivedCallback == nullptr) + if (m_impl->common._webMessageReceivedCallback == nullptr) return; std::string message = std::string("menu:") + menuItemId; diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/NSWindowBorderless.h b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/NSWindowBorderless.h index 4a689042f..9190c7b82 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/NSWindowBorderless.h +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/NSWindowBorderless.h @@ -7,10 +7,10 @@ * @file NSWindowBorderless.h * @brief NSWindow subclass that accepts first-mouse events and supports transparent backgrounds * - * Used when InfiniFrameInitParams::Transparent is set, allowing the WebView to render + * Used when InfiniFrameWindowInitParams::Transparent is set, allowing the WebView to render * over a fully transparent window background without the standard title bar and borders */ -#include "Runtime/Shared/Window/InfiniFrame.h" +#include "Runtime/Internal/Interop/Types/InfiniFrameWindow.h" // --------------------------------------------------------------------------------------------------------------------- // Code diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Notifications.UserNotifications.mm b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Notifications.UserNotifications.mm index dfe67998f..4c599a05f 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Notifications.UserNotifications.mm +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Notifications.UserNotifications.mm @@ -2,7 +2,7 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- #include "Window.Cocoa.Internal.h" -#include "Api/Utilities/ExportStringHelpers.h" +#include "Runtime/Internal/Interop/Exports/ExportStringHelpers.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/WebKit/WebKitHost.Cocoa.mm b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/WebKit/WebKitHost.Cocoa.mm index 2c10a96b5..02fdd7144 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/WebKit/WebKitHost.Cocoa.mm +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/WebKit/WebKitHost.Cocoa.mm @@ -43,21 +43,21 @@ } InfiniFrameWebView* infiniFrameWebView = (InfiniFrameWebView*)m_impl->_webview; - [infiniFrameWebView setInfiniFrameContextMenuEnabled:m_impl->_contextMenuEnabled ? YES : NO]; - [infiniFrameWebView setInfiniFrameZoomEnabled:m_impl->_zoomEnabled ? YES : NO]; + [infiniFrameWebView setInfiniFrameContextMenuEnabled:m_impl->common._contextMenuEnabled ? YES : NO]; + [infiniFrameWebView setInfiniFrameZoomEnabled:m_impl->common._zoomEnabled ? YES : NO]; [m_impl->_webview setMagnification: m_impl->_zoom / 100.0]; - SetTransparentEnabled(m_impl->_transparentEnabled); - if (m_impl->_backgroundColorR != 0 || m_impl->_backgroundColorG != 0 || m_impl->_backgroundColorB != 0 || m_impl->_backgroundColorA != 0) - SetBackgroundColor(m_impl->_backgroundColorR, m_impl->_backgroundColorG, m_impl->_backgroundColorB, m_impl->_backgroundColorA); + SetTransparentEnabled(m_impl->common._transparentEnabled); + if (m_impl->common._backgroundColorR != 0 || m_impl->common._backgroundColorG != 0 || m_impl->common._backgroundColorB != 0 || m_impl->common._backgroundColorA != 0) + SetBackgroundColor(m_impl->common._backgroundColorR, m_impl->common._backgroundColorG, m_impl->common._backgroundColorB, m_impl->common._backgroundColorA); SEL setInspectableSelector = NSSelectorFromString(@"setInspectable:"); if ([m_impl->_webview respondsToSelector: setInspectableSelector]) { using SetInspectableFn = void (*)(id, SEL, BOOL); auto setInspectable = reinterpret_cast([m_impl->_webview methodForSelector: setInspectableSelector]); - setInspectable(m_impl->_webview, setInspectableSelector, m_impl->_webInspectorEnabled ? YES : NO); + setInspectable(m_impl->_webview, setInspectableSelector, m_impl->common._webInspectorEnabled ? YES : NO); } - else if (m_impl->_webInspectorEnabled) + else if (m_impl->common._webInspectorEnabled) { throw std::runtime_error("Web inspector mode requires macOS 13.3+ WKWebView runtime support."); } @@ -70,7 +70,7 @@ m_impl->_uiDelegate = [[UiDelegate alloc] init]; m_impl->_uiDelegate->infiniFrame = this; m_impl->_uiDelegate->window = m_impl->_window; - m_impl->_uiDelegate->webMessageReceivedCallback = m_impl->_webMessageReceivedCallback; + m_impl->_uiDelegate->webMessageReceivedCallback = m_impl->common._webMessageReceivedCallback; if (m_impl->_navigationDelegate == nil) m_impl->_navigationDelegate = [[NavigationDelegate alloc] init]; @@ -82,10 +82,10 @@ m_impl->_webview.UIDelegate = m_impl->_uiDelegate; m_impl->_webview.navigationDelegate = m_impl->_navigationDelegate; - if (!m_impl->_startUrl.empty()) - NavigateToUrl(const_cast(m_impl->_startUrl.c_str())); - else if (!m_impl->_startString.empty()) - NavigateToString(const_cast(m_impl->_startString.c_str())); + if (!m_impl->common._startUrl.empty()) + NavigateToUrl(const_cast(m_impl->common._startUrl.c_str())); + else if (!m_impl->common._startString.empty()) + NavigateToString(const_cast(m_impl->common._startString.c_str())); else { NSAlert *alert = [[[NSAlert alloc] init] autorelease]; diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Window.Cocoa.Internal.h b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Window.Cocoa.Internal.h index 940b3e00f..74ffb94b5 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Window.Cocoa.Internal.h +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Mac/Window.Cocoa.Internal.h @@ -13,8 +13,8 @@ #include #include -#include "Runtime/Shared/Window/InfiniFrameWindow.h" -#include "Runtime/Shared/Window/InfiniFrameWindowImpl.h" +#include "Runtime/Internal/Interop/Types/InfiniFrameWindow.h" +#include "Runtime/Internal/Window/CommonWindowState.h" @ class UiDelegate; @@ -44,7 +44,8 @@ size_t PooledMacHostCountForTesting(); // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- -struct InfiniFrameWindow::Impl : InfiniFrameWindowImpl { +struct InfiniFrameWindow::Impl { + CommonWindowState common; NSWindow* _window = nil; WKWebView* _webview = nil; WKWebViewConfiguration* _webviewConfiguration = nil; @@ -91,4 +92,4 @@ struct InfiniFrameWindow::Impl : InfiniFrameWindowImpl { void AddCustomScheme(const char* scheme, WebResourceRequestedCallback requestHandler); bool LeasePooledMacHost(const std::string& compatibilityKey); void ReturnPooledMacHost(); -}; \ No newline at end of file +}; diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Core/UiDispatcher.Win32.cpp b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Core/UiDispatcher.Win32.cpp index 3b0b30f8d..f6dc25ada 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Core/UiDispatcher.Win32.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Core/UiDispatcher.Win32.cpp @@ -4,7 +4,7 @@ #include "chrono" #include "Runtime/Platform/Windows/Window.Win32.Context.h" -#include "Runtime/Shared/Operations/NativeOperation.h" +#include "Runtime/Internal/Operations/NativeOperation.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -64,4 +64,4 @@ bool InfiniFrameWindow::ScheduleOperation(const std::shared_ptr delete retained; return false; -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Core/WindowCore.Win32.cpp b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Core/WindowCore.Win32.cpp index 4f1c18356..c222c2137 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Core/WindowCore.Win32.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Core/WindowCore.Win32.cpp @@ -8,6 +8,8 @@ #include "Runtime/Platform/Windows/DarkMode.h" #include "Runtime/Platform/Windows/Window.Win32.Context.h" +#include "Runtime/Internal/Interop/Types/InfiniFrameWindowInitParams.h" +#include "Runtime/Internal/Application/InfiniFrameApplication.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -57,6 +59,22 @@ namespace { }; } +HINSTANCE GetWindowModuleInstance() noexcept { + return _hInstance.load(std::memory_order_acquire); +} + +HWND GetMessageLoopRootWindowHandle() noexcept { + return messageLoopRootWindowHandle; +} + +void SetMessageLoopRootWindowHandle(const HWND hwnd) noexcept { + messageLoopRootWindowHandle = hwnd; +} + +const wchar_t* GetWindowClassName() noexcept { + return CLASS_NAME; +} + HBRUSH GetDarkBrush() { return BrushManager::instance().dark(); } @@ -84,7 +102,7 @@ void InfiniFrameWindow::Register(const HINSTANCE hInstance) { wcx.lpszClassName = CLASS_NAME; wcx.hIconSm = LoadIcon(hInstance, IDI_APPLICATION); - if (RegisterClassEx(&wcx) == 0) { + if (RegisterClassEx(&wcx) == 0 && GetLastError() != ERROR_CLASS_ALREADY_EXISTS) { throw std::runtime_error("RegisterClassEx failed for window class 'InfiniFrame'."); } @@ -94,10 +112,10 @@ void InfiniFrameWindow::Register(const HINSTANCE hInstance) { // Initializes native window lifecycle state from host-provided startup parameters. // Flow: // 1) Allocate implementation storage. -// 2) Validate ABI compatibility of InfiniFrameInitParams via StructSize. +// 2) Validate ABI compatibility of InfiniFrameWindowInitParams via StructSize. // 3) Configure window identity/notifications and startup payload values. // 4) Continue with remaining platform/window initialization in this constructor. -InfiniFrameWindow::InfiniFrameWindow(InfiniFrameInitParams* initParams) { +InfiniFrameWindow::InfiniFrameWindow(InfiniFrameWindowInitParams* initParams) { // Backing implementation object must exist before any field assignment. m_impl = std::make_unique(); @@ -106,15 +124,19 @@ InfiniFrameWindow::InfiniFrameWindow(InfiniFrameInitParams* initParams) { WinToastLib::setDebugOutputEnabled(false); // Fail fast if caller and native side disagree on struct layout/version. - if (initParams->StructSize != sizeof(InfiniFrameInitParams)) { + if (initParams->StructSize != sizeof(InfiniFrameWindowInitParams)) { throw std::invalid_argument( "Initial parameters passed are " + std::to_string(initParams->StructSize) + - " bytes, but expected " + std::to_string(sizeof(InfiniFrameInitParams)) + " bytes." + " bytes, but expected " + std::to_string(sizeof(InfiniFrameWindowInitParams)) + " bytes." ); } - if (initParams->WindowsAppUserModelId != nullptr && initParams->WindowsAppUserModelId[0] != '\0') { - const std::wstring appUserModelId = ToUTF16String(initParams->WindowsAppUserModelId); + _application = initParams->ApplicationInstance; + InfiniFrameApplication* application = _application; + const char* appUserModelIdValue = application == nullptr ? nullptr : application->GetAppUserModelId(); + + if (appUserModelIdValue != nullptr && appUserModelIdValue[0] != '\0') { + const std::wstring appUserModelId = ToUTF16String(appUserModelIdValue); m_impl->_windowsAppUserModelId = appUserModelId; const HRESULT result = SetCurrentProcessExplicitAppUserModelID(appUserModelId.c_str()); if (FAILED(result)) { @@ -129,56 +151,61 @@ InfiniFrameWindow::InfiniFrameWindow(InfiniFrameInitParams* initParams) { // Initialize window title and optional toast notification identity. if (initParams->Title != nullptr) { - m_impl->_windowTitle = ToUTF16String(initParams->Title); + m_impl->common._windowTitle = ToUTF16String(initParams->Title); if (initParams->NotificationsEnabled) { - WinToast::instance()->setAppName(m_impl->_windowTitle.c_str()); + WinToast::instance()->setAppName(m_impl->common._windowTitle.c_str()); } } // Capture startup URL (if provided) for initial navigation/bootstrap. if (initParams->StartUrl != nullptr) - m_impl->_startUrl = ToUTF16String(initParams->StartUrl); + m_impl->common._startUrl = ToUTF16String(initParams->StartUrl); // Capture startup string payload (if provided) for host-defined boot data. if (initParams->StartString != nullptr) - m_impl->_startString = ToUTF16String(initParams->StartString); + m_impl->common._startString = ToUTF16String(initParams->StartString); if (initParams->TemporaryFilesPath != nullptr) m_impl->_temporaryFilesPath = ToUTF16String(initParams->TemporaryFilesPath); if (initParams->UserAgent != nullptr) - m_impl->_userAgent = ToUTF16String(initParams->UserAgent); + m_impl->common._userAgent = ToUTF16String(initParams->UserAgent); if (initParams->BrowserControlInitParameters != nullptr) - m_impl->_browserControlInitParameters = ToUTF16String(initParams->BrowserControlInitParameters); - - if (initParams->WebView2RuntimePath != nullptr) - m_impl->_webView2RuntimePath = ToUTF16String(initParams->WebView2RuntimePath); - - if (initParams->NotificationRegistrationId != nullptr) - m_impl->_notificationRegistrationId = ToUTF16String(initParams->NotificationRegistrationId); - m_impl->_remoteDebuggingPort = initParams->RemoteDebuggingPort; - - m_impl->_transparentEnabled = initParams->Transparent; - m_impl->_backgroundColorR = initParams->BackgroundColorR; - m_impl->_backgroundColorG = initParams->BackgroundColorG; - m_impl->_backgroundColorB = initParams->BackgroundColorB; - m_impl->_backgroundColorA = initParams->BackgroundColorA; - m_impl->_contextMenuEnabled = initParams->ContextMenuEnabled; - m_impl->_zoomEnabled = initParams->ZoomEnabled; - m_impl->_devToolsEnabled = initParams->DevToolsEnabled; - m_impl->_grantBrowserPermissions = initParams->GrantBrowserPermissions; - m_impl->_mediaAutoplayEnabled = initParams->MediaAutoplayEnabled; - m_impl->_fileSystemAccessEnabled = initParams->FileSystemAccessEnabled; - m_impl->_webSecurityEnabled = initParams->WebSecurityEnabled; - m_impl->_javascriptClipboardAccessEnabled = initParams->JavascriptClipboardAccessEnabled; - m_impl->_mediaStreamEnabled = initParams->MediaStreamEnabled; - m_impl->_smoothScrollingEnabled = initParams->SmoothScrollingEnabled; - m_impl->_ignoreCertificateErrorsEnabled = initParams->IgnoreCertificateErrorsEnabled; - m_impl->_statusBarEnabled = initParams->StatusBarEnabled; - m_impl->_browserShortcutsEnabled = initParams->BrowserShortcutsEnabled; + m_impl->common._browserControlInitParameters = ToUTF16String(initParams->BrowserControlInitParameters); + + const char* webView2RuntimePath = application == nullptr ? nullptr : application->GetWebView2RuntimePath(); + if (webView2RuntimePath != nullptr && webView2RuntimePath[0] != '\0') + m_impl->common._webView2RuntimePath = ToUTF16String(webView2RuntimePath); + + const char* notificationRegistrationId = application == nullptr ? nullptr : application->GetNotificationRegistrationId(); + if (notificationRegistrationId != nullptr && notificationRegistrationId[0] != '\0') + m_impl->_notificationRegistrationId = ToUTF16String(notificationRegistrationId); + m_impl->common._remoteDebuggingPort = initParams->RemoteDebuggingPort; + + m_impl->common._transparentEnabled = initParams->Transparent; + m_impl->common._backgroundColorR = initParams->BackgroundColorR; + m_impl->common._backgroundColorG = initParams->BackgroundColorG; + m_impl->common._backgroundColorB = initParams->BackgroundColorB; + m_impl->common._backgroundColorA = initParams->BackgroundColorA; + m_impl->common._contextMenuEnabled = initParams->ContextMenuEnabled; + m_impl->common._zoomEnabled = initParams->ZoomEnabled; + m_impl->common._devToolsEnabled = initParams->DevToolsEnabled; + m_impl->common._grantBrowserPermissions = initParams->GrantBrowserPermissions; + m_impl->common._mediaAutoplayEnabled = initParams->MediaAutoplayEnabled; + m_impl->common._fileSystemAccessEnabled = initParams->FileSystemAccessEnabled; + m_impl->common._webSecurityEnabled = initParams->WebSecurityEnabled; + m_impl->common._javascriptClipboardAccessEnabled = initParams->JavascriptClipboardAccessEnabled; + m_impl->common._mediaStreamEnabled = initParams->MediaStreamEnabled; + m_impl->common._smoothScrollingEnabled = initParams->SmoothScrollingEnabled; + m_impl->common._ignoreCertificateErrorsEnabled = initParams->IgnoreCertificateErrorsEnabled; + m_impl->common._statusBarEnabled = initParams->StatusBarEnabled; + m_impl->common._browserShortcutsEnabled = initParams->BrowserShortcutsEnabled; m_impl->_notificationsEnabled = initParams->NotificationsEnabled; - m_impl->_defaultNotificationIcon = ToUTF8String(initParams->DefaultNotificationIcon); + const char* defaultNotificationIcon = application == nullptr + ? initParams->DefaultNotificationIcon + : application->GetDefaultNotificationIcon(); + m_impl->_platformDefaultNotificationIcon = ToUTF8String(defaultNotificationIcon); m_impl->_zoom = initParams->Zoom; m_impl->_minWidth = initParams->MinWidth; @@ -186,28 +213,28 @@ InfiniFrameWindow::InfiniFrameWindow(InfiniFrameInitParams* initParams) { m_impl->_maxWidth = initParams->MaxWidth; m_impl->_maxHeight = initParams->MaxHeight; - m_impl->_webMessageReceivedCallback = initParams->WebMessageReceivedHandler; - m_impl->_resizedCallback = initParams->ResizedHandler; - m_impl->_maximizedCallback = initParams->MaximizedHandler; - m_impl->_restoredCallback = initParams->RestoredHandler; - m_impl->_minimizedCallback = initParams->MinimizedHandler; - m_impl->_movedCallback = initParams->MovedHandler; - m_impl->_closingCallback = initParams->ClosingHandler; - m_impl->_closedCallback = initParams->ClosedHandler; - m_impl->_focusInCallback = initParams->FocusInHandler; - m_impl->_focusOutCallback = initParams->FocusOutHandler; - m_impl->_debugEventCallback = initParams->DebugEventHandler; - m_impl->_customSchemeCallback = initParams->CustomSchemeHandler; - m_impl->_navigationStartingCallback = initParams->NavigationStartingHandler; - m_impl->_fileDroppedCallback = initParams->DragDropHandler; - m_impl->_dragDropEnabled = initParams->DragDropEnabled; - - for (std::size_t i = 0; i < InfiniFrameInitParams::MaxCustomSchemeNames; ++i) { + m_impl->common._webMessageReceivedCallback = initParams->WebMessageReceivedHandler; + m_impl->common._resizedCallback = initParams->ResizedHandler; + m_impl->common._maximizedCallback = initParams->MaximizedHandler; + m_impl->common._restoredCallback = initParams->RestoredHandler; + m_impl->common._minimizedCallback = initParams->MinimizedHandler; + m_impl->common._movedCallback = initParams->MovedHandler; + m_impl->common._closingCallback = initParams->ClosingHandler; + m_impl->common._closedCallback = initParams->ClosedHandler; + m_impl->common._focusInCallback = initParams->FocusInHandler; + m_impl->common._focusOutCallback = initParams->FocusOutHandler; + m_impl->common._debugEventCallback = initParams->DebugEventHandler; + m_impl->common._customSchemeCallback = initParams->CustomSchemeHandler; + m_impl->common._navigationStartingCallback = initParams->NavigationStartingHandler; + m_impl->common._fileDroppedCallback = initParams->DragDropHandler; + m_impl->common._dragDropEnabled = initParams->DragDropEnabled; + + for (std::size_t i = 0; i < InfiniFrameWindowInitParams::MaxCustomSchemeNames; ++i) { if (initParams->CustomSchemeNames[i] != nullptr) - m_impl->_customSchemeNames.emplace_back(ToUTF16String(initParams->CustomSchemeNames[i])); + m_impl->common._customSchemeNames.emplace_back(ToUTF16String(initParams->CustomSchemeNames[i])); } - m_impl->_parent = initParams->ParentInstance; + m_impl->common._parent = initParams->ParentInstance; int normalizedWidth = initParams->Width; int normalizedHeight = initParams->Height; @@ -259,19 +286,19 @@ InfiniFrameWindow::InfiniFrameWindow(InfiniFrameInitParams* initParams) { if (normalizedWidth < initParams->MinWidth && initParams->MinWidth > 0) normalizedWidth = initParams->MinWidth; - const HWND parentWindowHandle = ResolveParentWindowHandle(m_impl->_parent); + const HWND parentWindowHandle = ResolveParentWindowHandle(m_impl->common._parent); m_impl->_pendingOwnerHwnd = parentWindowHandle; - const HINSTANCE windowInstance = _hInstance.load(std::memory_order_acquire); + const HINSTANCE windowInstance = GetWindowModuleInstance(); m_impl->_hWnd = CreateWindowEx( - initParams->Transparent ? WS_EX_LAYERED : 0, CLASS_NAME, m_impl->_windowTitle.c_str(), + initParams->Transparent ? WS_EX_LAYERED : 0, GetWindowClassName(), m_impl->common._windowTitle.c_str(), initParams->Chromeless || initParams->FullScreen ? WS_POPUP : WS_OVERLAPPEDWINDOW, normalizedLeft, normalizedTop, normalizedWidth, normalizedHeight, nullptr, nullptr, windowInstance, this ); if (m_impl->_hWnd == nullptr) { throw std::runtime_error("CreateWindowEx failed to create the native window."); } - SetWindowTextW(m_impl->_hWnd, m_impl->_windowTitle.c_str()); + SetWindowTextW(m_impl->_hWnd, m_impl->common._windowTitle.c_str()); ApplyPendingOwnerWindow(m_impl.get(), L"ctor"); @@ -294,18 +321,14 @@ InfiniFrameWindow::InfiniFrameWindow(InfiniFrameInitParams* initParams) { SetTopmost(true); if (initParams->NotificationsEnabled) { - if (!m_impl->_windowsAppUserModelId.empty()) - WinToast::instance()->setAppUserModelId(m_impl->_windowsAppUserModelId.c_str()); - else if (!m_impl->_notificationRegistrationId.empty()) - WinToast::instance()->setAppUserModelId(m_impl->_notificationRegistrationId.c_str()); - else - WinToast::instance()->setAppUserModelId(m_impl->_windowTitle.c_str()); - m_impl->_toastHandler = std::make_unique(this); - WinToast::instance()->initialize(); + if (application == nullptr) + WinToast::instance()->initialize(); + else + application->EnsureNotificationsInitialized(initParams->Title); } - m_impl->_dialog = std::make_unique(this); + m_impl->common._dialog = std::make_unique(this); if (initParams->DragDropEnabled) { DragAcceptFiles(m_impl->_hWnd, TRUE); @@ -315,20 +338,31 @@ InfiniFrameWindow::InfiniFrameWindow(InfiniFrameInitParams* initParams) { ApplyInitMenuBar(initParams->MenuBarJson); } + if (_application != nullptr) + _application->TrackWindow(this); + bool isAlreadyShown = initParams->Minimized || initParams->Maximized; Show(isAlreadyShown); } -InfiniFrameWindow::~InfiniFrameWindow() {} +InfiniFrameWindow::~InfiniFrameWindow() { + if (m_impl != nullptr && m_impl->_hWnd != nullptr) { + SetWindowLongPtr(m_impl->_hWnd, GWLP_USERDATA, 0); + DestroyWindow(m_impl->_hWnd); + m_impl->_hWnd = nullptr; + } + if (_application != nullptr) + _application->UntrackWindow(this); +} -InfiniFrameWindowImpl* InfiniFrameWindow::ImplBase() noexcept { - return m_impl.get(); +CommonWindowState* GetCommonWindowState(InfiniFrameWindow* window) noexcept { + return &window->m_impl->common; } -const InfiniFrameWindowImpl* InfiniFrameWindow::ImplBase() const noexcept { - return m_impl.get(); +const CommonWindowState* GetCommonWindowState(const InfiniFrameWindow* window) noexcept { + return &window->m_impl->common; } HWND InfiniFrameWindow::getHwnd() { return m_impl->_hWnd; -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Core/WindowLifecycle.Win32.cpp b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Core/WindowLifecycle.Win32.cpp index 4c5ca00ba..78bfa6c2f 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Core/WindowLifecycle.Win32.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Core/WindowLifecycle.Win32.cpp @@ -29,7 +29,7 @@ void InfiniFrameWindow::WaitForExit() { auto* impl = m_impl.get(); ApplyPendingOwnerWindow(impl, L"wait_for_exit"); - messageLoopRootWindowHandle = impl->_hWnd; + SetMessageLoopRootWindowHandle(impl->_hWnd); TraceTeardown(L"WaitForExit start instance=%p hwnd=%p", this, impl->_hWnd); MSG msg = {}; @@ -46,21 +46,16 @@ void InfiniFrameWindow::WaitForExit() { DispatchMessage(&msg); } - messageLoopRootWindowHandle = nullptr; + SetMessageLoopRootWindowHandle(nullptr); TraceTeardown(L"WaitForExit end instance=%p hwnd=%p", this, impl->_hWnd); } -namespace { - DWORD CALLBACK CompleteTeardown(void* context) { - static_cast(context)->SignalTeardown(); - return 0; - } -} - void InfiniFrameWindow::ScheduleTeardownCompletion() { CompleteOperationsForClose(); CompleteNavigationForClose(); CompleteDialogsForClose(); - if (!QueueUserWorkItem(CompleteTeardown, this, WT_EXECUTEONLYONCE)) - SignalTeardown(); -} \ No newline at end of file + // WM_NCDESTROY is the native lifetime boundary. Signal the managed milestone + // before returning from that callback; managed completion is queued by the + // reverse callback, so native handle release cannot happen re-entrantly. + SignalTeardown(); +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Core/WindowProc.Win32.cpp b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Core/WindowProc.Win32.cpp index f9d0e1213..3c701a188 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Core/WindowProc.Win32.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Core/WindowProc.Win32.cpp @@ -3,8 +3,9 @@ // --------------------------------------------------------------------------------------------------------------------- #include "Runtime/Platform/Windows/DarkMode.h" #include "Runtime/Platform/Windows/Window.Win32.Context.h" -#include "Runtime/Shared/Operations/NativeOperation.h" -#include "Runtime/Shared/Window/InfiniFrameWindow.h" +#include "Runtime/Internal/Application/InfiniFrameApplication.h" +#include "Runtime/Internal/Operations/NativeOperation.h" +#include "Runtime/Internal/Interop/Types/InfiniFrameWindow.h" #include // --------------------------------------------------------------------------------------------------------------------- @@ -171,7 +172,9 @@ namespace { instance->CloseWebView(); instance->InvokeClosed(); TraceTeardown(L"WM_DESTROY end hwnd=%p instance=%p", hwnd, instance); - if (hwnd == messageLoopRootWindowHandle) + if (InfiniFrameApplication* application = instance->GetApplication()) + application->NotifyWindowClosed(instance); + else if (hwnd == GetMessageLoopRootWindowHandle()) PostQuitMessage(0); return 0; @@ -259,7 +262,7 @@ namespace { // - applies per-monitor DPI resize recommendations // - forwards focus and close events to the owning instance // - paints the window background according to current theme -LRESULT CALLBACK WindowProc(const HWND hwnd, const UINT uMsg, const WPARAM wParam, const LPARAM lParam) { +LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_NCCREATE: { return initialize_window_instance(hwnd, lParam); @@ -291,29 +294,31 @@ LRESULT CALLBACK WindowProc(const HWND hwnd, const UINT uMsg, const WPARAM wPara } case WM_SIZE: { if (auto* instance = LookupWindowInstance(hwnd)) - handle_window_size_change(instance, instance->m_impl.get(), wParam); + handle_window_size_change(instance, InfiniFrameWindowAccess::Get(*instance), wParam); break; } case WM_MOVE: { if (auto* instance = LookupWindowInstance(hwnd)) - update_window_position(instance, instance->m_impl.get()); + update_window_position(instance, InfiniFrameWindowAccess::Get(*instance)); break; } case WM_CLOSE: { if (auto* instance = LookupWindowInstance(hwnd)) - return handle_window_close(hwnd, instance, instance->m_impl.get()); + return handle_window_close(hwnd, instance, InfiniFrameWindowAccess::Get(*instance)); return 0; } case WM_DESTROY: { if (auto* instance = LookupWindowInstance(hwnd)) - return handle_window_destruction(hwnd, instance, instance->m_impl.get()); - if (hwnd == messageLoopRootWindowHandle) + return handle_window_destruction(hwnd, instance, InfiniFrameWindowAccess::Get(*instance)); + if (InfiniFrameApplication* application = InfiniFrameApplication::GetInstance()) + application->NotifyWindowClosed(nullptr); + else if (hwnd == GetMessageLoopRootWindowHandle()) PostQuitMessage(0); return 0; } case WM_NCDESTROY: { if (auto* instance = LookupWindowInstance(hwnd)) - cleanup_window_instance(hwnd, instance, instance->m_impl.get()); + cleanup_window_instance(hwnd, instance, InfiniFrameWindowAccess::Get(*instance)); else SetWindowLongPtr(hwnd, GWLP_USERDATA, 0); break; @@ -340,4 +345,4 @@ LRESULT CALLBACK WindowProc(const HWND hwnd, const UINT uMsg, const WPARAM wPara } return DefWindowProc(hwnd, uMsg, wParam, lParam); -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Core/WindowState.Win32.cpp b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Core/WindowState.Win32.cpp index 9513796ea..74a90e9ca 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Core/WindowState.Win32.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Core/WindowState.Win32.cpp @@ -4,7 +4,7 @@ #include #include "Runtime/Platform/Windows/Window.Win32.Internal.h" -#include "Runtime/Shared/Utilities/StringCopy.h" +#include "Runtime/Internal/Utilities/StringCopy.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -69,7 +69,7 @@ void InfiniFrameWindow::GetMinSize(int* width, int* height) const { } const char* InfiniFrameWindow::GetTitle() const { - return AllocateUtf8FromWide(m_impl->_windowTitle); + return AllocateUtf8FromWide(m_impl->common._windowTitle); } const char* InfiniFrameWindow::GetCurrentUrl() const { @@ -185,7 +185,7 @@ void InfiniFrameWindow::SetFullScreen(const bool fullScreen) { void InfiniFrameWindow::SetIconFile(const char* filename) { std::wstring wideFilename = ToUTF16String(filename); - m_impl->_iconFileName = wideFilename; + m_impl->common._iconFileName = wideFilename; if (wideFilename.empty()) return; @@ -276,7 +276,7 @@ void InfiniFrameWindow::SetSize(const int width, const int height) { void InfiniFrameWindow::SetTitle(const char* title) { std::wstring wideTitle = ToUTF16String(title); - m_impl->_windowTitle = wideTitle; + m_impl->common._windowTitle = wideTitle; SetWindowText(m_impl->_hWnd, wideTitle.c_str()); if (m_impl->_notificationsEnabled) { WinToastLib::WinToast::instance()->setAppName(wideTitle.c_str()); @@ -298,7 +298,7 @@ void InfiniFrameWindow::SetTopmost(const bool topmost) { void InfiniFrameWindow::SetZoom(const int zoom) { // Software guard: respect EnableZoom(false) to match cross-platform API semantics. - if (!m_impl->_zoomEnabled) + if (!m_impl->common._zoomEnabled) return; // Clamp to valid range (25-500%) to match Linux/macOS behavior. @@ -339,4 +339,4 @@ void InfiniFrameWindow::SetFocused() { AttachThreadInput(fgThread, thisThread, FALSE); FocusWebView2(); -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Dialog.cpp b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Dialog.cpp index 25207cfd6..c461b58db 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Dialog.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Dialog.cpp @@ -1,9 +1,10 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -#include "Runtime/Shared/Window/InfiniFrame.h" -#include "Runtime/Shared/Operations/DialogOperation.h" -#include "Runtime/Shared/Utilities/StringArrayCopy.h" +#include "Runtime/Internal/Interop/Types/InfiniFrameWindow.h" +#include "Runtime/Internal/Window/InfiniFrameDialog.h" +#include "Runtime/Internal/Operations/DialogOperation.h" +#include "Runtime/Internal/Utilities/StringArrayCopy.h" #include "Runtime/Platform/Windows/Window.Win32.Context.h" #include @@ -13,6 +14,12 @@ #include #include #include + +struct InfiniFrameDialog::Impl { + InfiniFrameWindow* window = nullptr; +}; + +#define _window m_impl->window // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- @@ -133,6 +140,7 @@ inline HANDLE NewStyleContext::Create() { } InfiniFrameDialog::InfiniFrameDialog(InfiniFrameWindow* window) { + m_impl = std::make_unique(); _window = window; } @@ -651,4 +659,4 @@ void InfiniFrameWindow::BeginShowMessage( operation->CompleteMessage(value); CoUninitialize(); }).detach(); -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Menu.Win32.cpp b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Menu.Win32.cpp index b479b1b96..11ec138fe 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Menu.Win32.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Menu.Win32.cpp @@ -215,7 +215,7 @@ void InfiniFrameWindow::SetMenuItemVisibleById(const char* menuItemId, const boo } void InfiniFrameWindow::ClickMenuItemById(const char* menuItemId) { - if (m_impl->_webMessageReceivedCallback == nullptr) + if (m_impl->common._webMessageReceivedCallback == nullptr) return; std::string message = std::string("menu:") + menuItemId; @@ -229,7 +229,7 @@ void InfiniFrameWindow::HandleMenuCommand(const WPARAM wParam) { if (it == m_impl->_menuCommandIdToItemId.end()) return; - if (m_impl->_webMessageReceivedCallback == nullptr) + if (m_impl->common._webMessageReceivedCallback == nullptr) return; std::string message = std::string("menu:") + it->second; diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Notifications.WinToast.cpp b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Notifications.WinToast.cpp index 5ab5798e6..deca25d89 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Notifications.WinToast.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Notifications.WinToast.cpp @@ -2,7 +2,7 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- #include "Runtime/Platform/Windows/Window.Win32.Context.h" -#include "Api/Utilities/ExportStringHelpers.h" +#include "Runtime/Internal/Interop/Exports/ExportStringHelpers.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -17,8 +17,8 @@ void InfiniFrameWindow::ShowNotification(const char* title, const char* body) { WinToastTemplate(WinToastTemplate::ImageAndText02); toast.setTextField(wideTitle.c_str(), WinToastTemplate::FirstLine); toast.setTextField(wideBody.c_str(), WinToastTemplate::SecondLine); - if (!m_impl->_iconFileName.empty()) - toast.setImagePath(m_impl->_iconFileName); + if (!m_impl->common._iconFileName.empty()) + toast.setImagePath(m_impl->common._iconFileName); WinToast::instance()->showToast(toast, m_impl->_toastHandler.get()); } } @@ -42,8 +42,8 @@ void InfiniFrameWindow::ShowNotificationWithOptions( const char* iconStr = NullToEmpty(iconPath); if (iconStr[0] != '\0') { toast.setImagePath(ToUTF16String(iconStr)); - } else if (!m_impl->_iconFileName.empty()) { - toast.setImagePath(m_impl->_iconFileName); + } else if (!m_impl->common._iconFileName.empty()) { + toast.setImagePath(m_impl->common._iconFileName); } if (urgency >= 0 && urgency <= 3) { @@ -83,8 +83,8 @@ void InfiniFrameWindow::BeginShowNotification( const char* iconStr = NullToEmpty(iconPath); if (iconStr[0] != '\0') { toast.setImagePath(ToUTF16String(iconStr)); - } else if (!m_impl->_iconFileName.empty()) { - toast.setImagePath(m_impl->_iconFileName); + } else if (!m_impl->common._iconFileName.empty()) { + toast.setImagePath(m_impl->common._iconFileName); } if (urgency >= 0 && urgency <= 3) { @@ -110,4 +110,4 @@ void InfiniFrameWindow::CancelNotification(const uint64_t operationId, bool* can (void)operationId; if (canceled) *canceled = false; -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Taskbar.Win32.cpp b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Taskbar.Win32.cpp index fb5f255a9..0ac9c4963 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Taskbar.Win32.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Taskbar.Win32.cpp @@ -5,7 +5,7 @@ #include #include -#include "Runtime/Shared/Window/InfiniFrameWindow.h" +#include "Runtime/Internal/Interop/Types/InfiniFrameWindow.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -136,4 +136,4 @@ void InfiniFrameWindow::GetTaskbarProgressSupported(bool* supported) const { *supported = true; } -#endif \ No newline at end of file +#endif diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/ToastHandler.h b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/ToastHandler.h index 69fbacff3..f8075a051 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/ToastHandler.h +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/ToastHandler.h @@ -4,7 +4,7 @@ // --------------------------------------------------------------------------------------------------------------------- #include -#include "Runtime/Shared/Window/InfiniFrameWindow.h" +#include "Runtime/Internal/Interop/Types/InfiniFrameWindow.h" #include "Dependencies/wintoastlib/wintoastlib.h" // --------------------------------------------------------------------------------------------------------------------- // Code @@ -60,4 +60,4 @@ class WinToastHandler final : public IWinToastHandler { /** @brief Called when the notification fails to display */ void toastFailed() const override {} -}; \ No newline at end of file +}; diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/WebView/WebView2Attach.Win32.cpp b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/WebView/WebView2Attach.Win32.cpp index 44e21b44c..1c538b592 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/WebView/WebView2Attach.Win32.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/WebView/WebView2Attach.Win32.cpp @@ -31,7 +31,7 @@ void InfiniFrameWindow::Show(const bool isAlreadyShown) { UpdateWindow(m_impl->_hWnd); if (!m_impl->_webviewController) { - if (!m_impl->_webView2RuntimePath.empty() || EnsureWebViewIsInstalled()) + if (!m_impl->common._webView2RuntimePath.empty() || EnsureWebViewIsInstalled()) AttachWebView(); else throw std::runtime_error("WebView2 Runtime is not installed and automatic installation failed."); @@ -68,34 +68,34 @@ void InfiniFrameWindow::AttachWebView() { m_impl->_isWebView2Initializing = true; // Keep a local owned value alive until environment creation has consumed it. - const std::wstring configuredRuntimePath = m_impl->_webView2RuntimePath; + const std::wstring configuredRuntimePath = m_impl->common._webView2RuntimePath; PCWSTR runtimePath = configuredRuntimePath.empty() ? nullptr : configuredRuntimePath.c_str(); // Compose WebView2 command-line switches from current window/browser options. // This string is passed to environment creation and controls browser process behavior. std::wstring startupString; - if (!m_impl->_userAgent.empty()) - startupString += L"--user-agent=\"" + m_impl->_userAgent + L"\" "; - if (m_impl->_mediaAutoplayEnabled) + if (!m_impl->common._userAgent.empty()) + startupString += L"--user-agent=\"" + m_impl->common._userAgent + L"\" "; + if (m_impl->common._mediaAutoplayEnabled) startupString += L"--autoplay-policy=no-user-gesture-required "; - if (m_impl->_fileSystemAccessEnabled) + if (m_impl->common._fileSystemAccessEnabled) startupString += L"--allow-file-access-from-files "; - if (!m_impl->_webSecurityEnabled) + if (!m_impl->common._webSecurityEnabled) startupString += L"--disable-web-security "; - if (m_impl->_javascriptClipboardAccessEnabled) + if (m_impl->common._javascriptClipboardAccessEnabled) startupString += L"--enable-javascript-clipboard-access "; - if (m_impl->_mediaStreamEnabled) + if (m_impl->common._mediaStreamEnabled) startupString += L"--enable-usermedia-screen-capturing "; - if (!m_impl->_smoothScrollingEnabled) + if (!m_impl->common._smoothScrollingEnabled) startupString += L"--disable-smooth-scrolling "; - if (m_impl->_ignoreCertificateErrorsEnabled) + if (m_impl->common._ignoreCertificateErrorsEnabled) startupString += L"--ignore-certificate-errors "; - if (!m_impl->_browserControlInitParameters.empty()) - startupString += m_impl->_browserControlInitParameters; //e.g.--hide-scrollbars - if (m_impl->_remoteDebuggingPort > 0) { + if (!m_impl->common._browserControlInitParameters.empty()) + startupString += m_impl->common._browserControlInitParameters; //e.g.--hide-scrollbars + if (m_impl->common._remoteDebuggingPort > 0) { startupString += std::format( L" --remote-debugging-address=127.0.0.1 --remote-debugging-port={}", - m_impl->_remoteDebuggingPort); + m_impl->common._remoteDebuggingPort); } auto options = Microsoft::WRL::Make(); @@ -200,11 +200,11 @@ void InfiniFrameWindow::AttachWebView() { if (fired) return; fired = true; - if (!self->m_impl->_startUrl.empty()) - self->m_impl->_webviewWindow->Navigate(self->m_impl->_startUrl.c_str()); - else if (!self->m_impl->_startString.empty()) + if (!self->m_impl->common._startUrl.empty()) + self->m_impl->_webviewWindow->Navigate(self->m_impl->common._startUrl.c_str()); + else if (!self->m_impl->common._startString.empty()) self->m_impl->_webviewWindow->NavigateToString( - self->m_impl->_startString.c_str() + self->m_impl->common._startString.c_str() ); else { OutputDebugStringW( @@ -217,8 +217,11 @@ void InfiniFrameWindow::AttachWebView() { auto nav = std::make_shared(NavigateOnce{this}); HRESULT settingsResult = ApplyInitialWebViewSettings(); - if (FAILED(settingsResult)) + if (FAILED(settingsResult)) { + m_impl->_isWebView2Initializing = false; + SignalReadyFailure(); return settingsResult; + } EventRegistrationToken webMessageToken; m_impl->_webviewWindow->add_WebMessageReceived( @@ -240,7 +243,7 @@ void InfiniFrameWindow::AttachWebView() { if (message.get() != nullptr && message.get()[0] != L'\0') { auto msgUtf8 = WideToUtf8(message.get()); auto srcUtf8 = WideToUtf8(source.get()); - m_impl->_webMessageReceivedCallback(msgUtf8.c_str(), srcUtf8.c_str()); + m_impl->common._webMessageReceivedCallback(msgUtf8.c_str(), srcUtf8.c_str()); } return S_OK; } @@ -266,7 +269,7 @@ void InfiniFrameWindow::AttachWebView() { args->get_PermissionKind(&permissionKind); if (permissionKind == COREWEBVIEW2_PERMISSION_KIND_AUTOPLAY) { args->put_State( - m_impl->_mediaAutoplayEnabled + m_impl->common._mediaAutoplayEnabled ? COREWEBVIEW2_PERMISSION_STATE_ALLOW : COREWEBVIEW2_PERMISSION_STATE_DENY ); @@ -274,7 +277,7 @@ void InfiniFrameWindow::AttachWebView() { } #endif - if (m_impl->_grantBrowserPermissions) + if (m_impl->common._grantBrowserPermissions) args->put_State(COREWEBVIEW2_PERMISSION_STATE_ALLOW); return S_OK; } @@ -296,7 +299,7 @@ void InfiniFrameWindow::AttachWebView() { args->get_NavigationId(&navigationId); BindNavigationBackendId(navigationId); - if (m_impl->_navigationStartingCallback == nullptr) + if (m_impl->common._navigationStartingCallback == nullptr) return S_OK; wil::unique_cotaskmem_string uri; @@ -311,7 +314,7 @@ void InfiniFrameWindow::AttachWebView() { int isMainFrame = 1; auto uriUtf8 = WideToUtf8(uri.get()); - int cancel = m_impl->_navigationStartingCallback( + int cancel = m_impl->common._navigationStartingCallback( uriUtf8.c_str(), isUserInitiated ? 1 : 0, isRedirected ? 1 : 0, isMainFrame ); if (cancel) { @@ -490,4 +493,4 @@ void InfiniFrameWindow::AttachWebView() { LPCTSTR errMsg = err.ErrorMessage(); MessageBox(m_impl->_hWnd, errMsg, L"Error instantiating webview", MB_OK); } -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/WebView/WebView2CustomSchemes.Win32.cpp b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/WebView/WebView2CustomSchemes.Win32.cpp index f64621a63..71a206465 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/WebView/WebView2CustomSchemes.Win32.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/WebView/WebView2CustomSchemes.Win32.cpp @@ -4,7 +4,8 @@ #include #include "Runtime/Platform/Windows/Window.Win32.Context.h" -#include "Runtime/Shared/WebView/CustomSchemeResponse.h" +#include "Runtime/Internal/WebView/CustomSchemeResponse.h" +#include "Runtime/Internal/Interop/Types/InfiniFrameWindowInitParams.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -12,7 +13,7 @@ using namespace Microsoft::WRL; bool InfiniFrameWindow::RegisterCustomSchemesOnOptions(ICoreWebView2EnvironmentOptions* options) { bool requiresAppSchemeRegistration = std::any_of( - m_impl->_customSchemeNames.begin(), m_impl->_customSchemeNames.end(), + m_impl->common._customSchemeNames.begin(), m_impl->common._customSchemeNames.end(), [](const std::wstring& schemeName) { return _wcsicmp(schemeName.c_str(), L"app") == 0; } @@ -20,14 +21,14 @@ bool InfiniFrameWindow::RegisterCustomSchemesOnOptions(ICoreWebView2EnvironmentO bool appSchemeRegistrationSupported = false; // Register custom schemes with WebView2 so top-level navigations like app://... are allowed. - if (!m_impl->_customSchemeNames.empty()) { + if (!m_impl->common._customSchemeNames.empty()) { wil::com_ptr options4; if (SUCCEEDED(options->QueryInterface(IID_PPV_ARGS(&options4))) && options4) { appSchemeRegistrationSupported = true; std::vector> registrations; - registrations.reserve(m_impl->_customSchemeNames.size()); + registrations.reserve(m_impl->common._customSchemeNames.size()); - for (const auto& schemeName : m_impl->_customSchemeNames) { + for (const auto& schemeName : m_impl->common._customSchemeNames) { auto registration = Microsoft::WRL::Make(schemeName.c_str()); if (!registration) continue; @@ -141,15 +142,15 @@ void InfiniFrameWindow::AttachCustomSchemeHandler() { if (colonPos > 0) { std::wstring scheme = uriString.substr(0, colonPos); auto it = std::find( - m_impl->_customSchemeNames.begin(), m_impl->_customSchemeNames.end(), + m_impl->common._customSchemeNames.begin(), m_impl->common._customSchemeNames.end(), scheme ); - if (it != m_impl->_customSchemeNames.end() && - m_impl->_customSchemeCallback != nullptr) { + if (it != m_impl->common._customSchemeNames.end() && + m_impl->common._customSchemeCallback != nullptr) { CustomSchemeResponse managedResponse{}; auto uriUtf8 = WideToUtf8(uriString.c_str()); - const int handled = m_impl->_customSchemeCallback( + const int handled = m_impl->common._customSchemeCallback( uriUtf8.c_str(), &managedResponse ); infiniframe::CustomSchemeResponseLease responseLease(managedResponse); @@ -197,12 +198,12 @@ void InfiniFrameWindow::AttachCustomSchemeHandler() { void InfiniFrameWindow::AddCustomSchemeName(const char* scheme) { if (scheme == nullptr) return; - if (m_impl->_customSchemeNames.size() >= InfiniFrameInitParams::MaxCustomSchemeNames) + if (m_impl->common._customSchemeNames.size() >= InfiniFrameWindowInitParams::MaxCustomSchemeNames) return; std::wstring wide = ToUTF16String(scheme); - for (const auto& existing : m_impl->_customSchemeNames) { + for (const auto& existing : m_impl->common._customSchemeNames) { if (_wcsicmp(existing.c_str(), wide.c_str()) == 0) return; } - m_impl->_customSchemeNames.emplace_back(std::move(wide)); -} \ No newline at end of file + m_impl->common._customSchemeNames.emplace_back(std::move(wide)); +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/WebView/WebView2Runtime.Win32.cpp b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/WebView/WebView2Runtime.Win32.cpp index fb23bf85b..6ac36ff54 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/WebView/WebView2Runtime.Win32.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/WebView/WebView2Runtime.Win32.cpp @@ -52,5 +52,5 @@ void InfiniFrameWindow::SetWebView2RuntimePath(const char* pathToWebView2) { if (pathToWebView2 == nullptr) return; - m_impl->_webView2RuntimePath = Utf8ToWide(pathToWebView2); + m_impl->common._webView2RuntimePath = Utf8ToWide(pathToWebView2); } \ No newline at end of file diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/WebView/WebView2Settings.Win32.cpp b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/WebView/WebView2Settings.Win32.cpp index c0cced65e..e5e9fd00e 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/WebView/WebView2Settings.Win32.cpp +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/WebView/WebView2Settings.Win32.cpp @@ -16,22 +16,21 @@ HRESULT InfiniFrameWindow::ApplyInitialWebViewSettings() { settings->put_AreDefaultScriptDialogsEnabled(TRUE); settings->put_IsWebMessageEnabled(TRUE); - if (!m_impl->_contextMenuEnabled) + if (!m_impl->common._contextMenuEnabled) SetContextMenuEnabled(false); - if (!m_impl->_zoomEnabled) + if (!m_impl->common._zoomEnabled) SetZoomEnabled(false); - if (!m_impl->_devToolsEnabled) + if (!m_impl->common._devToolsEnabled) SetDevToolsEnabled(false); - if (!m_impl->_statusBarEnabled) + if (!m_impl->common._statusBarEnabled) SetStatusBarEnabled(false); - if (!m_impl->_browserShortcutsEnabled) + if (!m_impl->common._browserShortcutsEnabled) SetBrowserShortcutsEnabled(false); - if (m_impl->_transparentEnabled) + if (m_impl->common._transparentEnabled) SetTransparentEnabled(true); - if (m_impl->_backgroundColorR != 0 || m_impl->_backgroundColorG != 0 || m_impl->_backgroundColorB != 0 || m_impl-> - _backgroundColorA != 0) + if (m_impl->common._backgroundColorR != 0 || m_impl->common._backgroundColorG != 0 || m_impl->common._backgroundColorB != 0 || m_impl->common._backgroundColorA != 0) SetBackgroundColor( - m_impl->_backgroundColorR, m_impl->_backgroundColorG, m_impl->_backgroundColorB, m_impl->_backgroundColorA); + m_impl->common._backgroundColorR, m_impl->common._backgroundColorG, m_impl->common._backgroundColorB, m_impl->common._backgroundColorA); if (m_impl->_zoom != 100) SetZoom(m_impl->_zoom); @@ -40,12 +39,12 @@ HRESULT InfiniFrameWindow::ApplyInitialWebViewSettings() { void InfiniFrameWindow::GetTransparentEnabled(bool* enabled) const { if (!m_impl->_webviewController) { - *enabled = m_impl->_transparentEnabled; + *enabled = m_impl->common._transparentEnabled; return; } wil::com_ptr controller2; if (FAILED(m_impl->_webviewController->QueryInterface(&controller2)) || !controller2) { - *enabled = m_impl->_transparentEnabled; + *enabled = m_impl->common._transparentEnabled; return; } COREWEBVIEW2_COLOR backgroundColor; @@ -55,7 +54,7 @@ void InfiniFrameWindow::GetTransparentEnabled(bool* enabled) const { void InfiniFrameWindow::GetContextMenuEnabled(bool* enabled) const { if (!m_impl->_webviewWindow) { - *enabled = m_impl->_contextMenuEnabled; + *enabled = m_impl->common._contextMenuEnabled; return; } wil::com_ptr settings; @@ -68,7 +67,7 @@ void InfiniFrameWindow::GetContextMenuEnabled(bool* enabled) const { void InfiniFrameWindow::GetZoomEnabled(bool* enabled) const { if (!m_impl->_webviewWindow) { - *enabled = m_impl->_zoomEnabled; + *enabled = m_impl->common._zoomEnabled; return; } wil::com_ptr settings; @@ -81,7 +80,7 @@ void InfiniFrameWindow::GetZoomEnabled(bool* enabled) const { void InfiniFrameWindow::GetStatusBarEnabled(bool* enabled) const { if (!m_impl->_webviewWindow) { - *enabled = m_impl->_statusBarEnabled; + *enabled = m_impl->common._statusBarEnabled; return; } wil::com_ptr settings; @@ -94,7 +93,7 @@ void InfiniFrameWindow::GetStatusBarEnabled(bool* enabled) const { void InfiniFrameWindow::GetDevToolsEnabled(bool* enabled) const { if (!m_impl->_webviewWindow) { - *enabled = m_impl->_devToolsEnabled; + *enabled = m_impl->common._devToolsEnabled; return; } wil::com_ptr settings; @@ -106,7 +105,7 @@ void InfiniFrameWindow::GetDevToolsEnabled(bool* enabled) const { } void InfiniFrameWindow::SetTransparentEnabled(const bool enabled) { - m_impl->_transparentEnabled = enabled; + m_impl->common._transparentEnabled = enabled; if (!m_impl->_webviewController || !m_impl->_webviewWindow) return; wil::com_ptr controller2; @@ -120,7 +119,7 @@ void InfiniFrameWindow::SetTransparentEnabled(const bool enabled) { } void InfiniFrameWindow::SetContextMenuEnabled(const bool enabled) { - m_impl->_contextMenuEnabled = enabled; + m_impl->common._contextMenuEnabled = enabled; if (!m_impl->_webviewWindow) return; wil::com_ptr settings; @@ -131,7 +130,7 @@ void InfiniFrameWindow::SetContextMenuEnabled(const bool enabled) { } void InfiniFrameWindow::SetMediaAutoplayEnabled(const bool enabled) { - m_impl->_mediaAutoplayEnabled = enabled; + m_impl->common._mediaAutoplayEnabled = enabled; if (!m_impl->_webviewWindow) return; @@ -141,7 +140,7 @@ void InfiniFrameWindow::SetMediaAutoplayEnabled(const bool enabled) { } void InfiniFrameWindow::SetUserAgent(const char* userAgent) { - m_impl->_userAgent = userAgent != nullptr ? ToUTF16String(userAgent) : L""; + m_impl->common._userAgent = userAgent != nullptr ? ToUTF16String(userAgent) : L""; if (!m_impl->_webviewWindow) return; @@ -153,12 +152,12 @@ void InfiniFrameWindow::SetUserAgent(const char* userAgent) { if (FAILED(settings->QueryInterface(&settings2)) || !settings2) return; - settings2->put_UserAgent(m_impl->_userAgent.empty() ? nullptr : m_impl->_userAgent.c_str()); + settings2->put_UserAgent(m_impl->common._userAgent.empty() ? nullptr : m_impl->common._userAgent.c_str()); m_impl->_webviewWindow->Reload(); } void InfiniFrameWindow::SetZoomEnabled(const bool enabled) { - m_impl->_zoomEnabled = enabled; + m_impl->common._zoomEnabled = enabled; if (!m_impl->_webviewWindow) return; wil::com_ptr settings; @@ -169,7 +168,7 @@ void InfiniFrameWindow::SetZoomEnabled(const bool enabled) { } void InfiniFrameWindow::SetStatusBarEnabled(const bool enabled) { - m_impl->_statusBarEnabled = enabled; + m_impl->common._statusBarEnabled = enabled; if (!m_impl->_webviewWindow) return; wil::com_ptr settings; @@ -180,7 +179,7 @@ void InfiniFrameWindow::SetStatusBarEnabled(const bool enabled) { } void InfiniFrameWindow::SetBrowserShortcutsEnabled(const bool enabled) { - m_impl->_browserShortcutsEnabled = enabled; + m_impl->common._browserShortcutsEnabled = enabled; if (!m_impl->_webviewWindow) return; const char* flag = enabled ? "true" : "false"; @@ -192,7 +191,7 @@ void InfiniFrameWindow::SetBrowserShortcutsEnabled(const bool enabled) { } void InfiniFrameWindow::SetDevToolsEnabled(const bool enabled) { - m_impl->_devToolsEnabled = enabled; + m_impl->common._devToolsEnabled = enabled; if (!m_impl->_webviewWindow) return; wil::com_ptr settings; @@ -203,10 +202,10 @@ void InfiniFrameWindow::SetDevToolsEnabled(const bool enabled) { } void InfiniFrameWindow::SetBackgroundColor(const uint8_t r, const uint8_t g, const uint8_t b, const uint8_t a) { - m_impl->_backgroundColorR = r; - m_impl->_backgroundColorG = g; - m_impl->_backgroundColorB = b; - m_impl->_backgroundColorA = a; + m_impl->common._backgroundColorR = r; + m_impl->common._backgroundColorG = g; + m_impl->common._backgroundColorB = b; + m_impl->common._backgroundColorA = a; if (!m_impl->_webviewController) return; @@ -218,4 +217,4 @@ void InfiniFrameWindow::SetBackgroundColor(const uint8_t r, const uint8_t g, con COREWEBVIEW2_COLOR bgColor = {a, r, g, b}; controller2->put_DefaultBackgroundColor(bgColor); m_impl->_webviewWindow->Reload(); -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Window.Win32.Context.h b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Window.Win32.Context.h index 52df07c36..140eaff2b 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Window.Win32.Context.h +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Window.Win32.Context.h @@ -9,7 +9,7 @@ #include -#include "Runtime/Shared/Window/InfiniFrameWindow.h" +#include "Runtime/Internal/Interop/Types/InfiniFrameWindow.h" #include "Runtime/Platform/Windows/Window.Win32.Internal.h" // --------------------------------------------------------------------------------------------------------------------- // Code @@ -17,9 +17,10 @@ inline constexpr UINT WM_USER_INVOKE = WM_USER + 0x0002; inline constexpr UINT WM_USER_DISPATCH_OPERATION = WM_USER + 0x0003; -extern std::atomic _hInstance; -extern thread_local HWND messageLoopRootWindowHandle; -extern const wchar_t* CLASS_NAME; +HINSTANCE GetWindowModuleInstance() noexcept; +HWND GetMessageLoopRootWindowHandle() noexcept; +void SetMessageLoopRootWindowHandle(HWND hwnd) noexcept; +const wchar_t* GetWindowClassName() noexcept; struct InvokeWaitInfo { std::mutex mutex; @@ -74,4 +75,4 @@ template void ApplyPendingOwnerWindow(TImpl* impl, const wchar_ L"ApplyPendingOwnerWindow success phase=%ls child=%p owner=%p childTid=%lu ownerTid=%lu prev=%p", phase, impl->_hWnd, impl->_pendingOwnerHwnd, childThreadId, ownerThreadId, reinterpret_cast(previousOwner) ); -} \ No newline at end of file +} diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Window.Win32.Internal.h b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Window.Win32.Internal.h index 9cbec8bdc..96b893488 100644 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Window.Win32.Internal.h +++ b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Platform/Windows/Window.Win32.Internal.h @@ -11,21 +11,22 @@ #include #include -#include "Runtime/Shared/Window/InfiniFrameWindow.h" -#include "Runtime/Shared/Window/InfiniFrameWindowImpl.h" +#include "Runtime/Internal/Interop/Types/InfiniFrameWindow.h" +#include "Runtime/Internal/Window/CommonWindowState.h" #include "Runtime/Platform/Windows/ToastHandler.h" -#include "Runtime/Shared/Utilities/Dimensions.h" +#include "Runtime/Internal/Utilities/Dimensions.h" // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- -struct InfiniFrameWindow::Impl : InfiniFrameWindowImpl { +struct InfiniFrameWindow::Impl { + CommonWindowState common; std::wstring _temporaryFilesPath; std::wstring _notificationRegistrationId; std::wstring _windowsAppUserModelId; bool _notificationsEnabled = false; - std::string _defaultNotificationIcon; + std::string _platformDefaultNotificationIcon; bool _isInitialized = false; bool _isWebView2Initializing = false; std::atomic _isClosingOrClosed = false; @@ -85,4 +86,10 @@ struct InfiniFrameWindow::Impl : InfiniFrameWindowImpl { std::unordered_map _menuItemIdToCommandId; std::unordered_map _menuCommandIdToItemId; UINT _nextMenuCommandId = 1; -}; \ No newline at end of file +}; + +struct InfiniFrameWindowAccess { + static InfiniFrameWindow::Impl* Get(InfiniFrameWindow& window) noexcept { + return window.m_impl.get(); + } +}; diff --git a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Window/InfiniFrame.h b/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Window/InfiniFrame.h deleted file mode 100644 index f3784a4c6..000000000 --- a/src/InfiniFrame.NativeBridge/Native/src/Runtime/Shared/Window/InfiniFrame.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -// --------------------------------------------------------------------------------------------------------------------- -// Imports -// --------------------------------------------------------------------------------------------------------------------- -#include "Runtime/Shared/Window/InfiniFrameInitParams.h" -#include "Runtime/Shared/Window/InfiniFrameWindow.h" -#include "Runtime/Shared/Window/InfiniFrameDialog.h" - -#include "Runtime/Shared/Types/Basic.h" -#include "Runtime/Shared/Types/Callbacks.h" - -#include "Runtime/Shared/Utilities/Dimensions.h" -#include "Runtime/Shared/Utilities/StringCopy.h" \ No newline at end of file diff --git a/src/InfiniFrame.NativeBridge/Native/tests/PublicAbiCompileBoundary.cpp b/src/InfiniFrame.NativeBridge/Native/tests/PublicAbiCompileBoundary.cpp new file mode 100644 index 000000000..e29fff88b --- /dev/null +++ b/src/InfiniFrame.NativeBridge/Native/tests/PublicAbiCompileBoundary.cpp @@ -0,0 +1,32 @@ +#include +#include + +#include "Runtime/Internal/Interop/Types/Basic.h" +#include "Runtime/Internal/Interop/Types/Callbacks.h" +#include "Runtime/Internal/Interop/Types/CustomSchemeResponse.h" +#include "Runtime/Internal/Interop/Types/DialogButtons.h" +#include "Runtime/Internal/Interop/Types/DialogIcon.h" +#include "Runtime/Internal/Interop/Types/DialogResult.h" +#include "Runtime/Internal/Interop/Types/InfiniFrameApplicationInitParams.h" +#include "Runtime/Internal/Interop/Types/InfiniFrameWindowInitParams.h" +#include "Runtime/Internal/Interop/Types/Monitor.h" + +static_assert(std::is_standard_layout_v); +static_assert(std::is_standard_layout_v); +static_assert(std::is_standard_layout_v); +static_assert(sizeof(bool) == 1); +static_assert(offsetof(InfiniFrameApplicationInitParams, WebView2RuntimePath) == 0); +static_assert(offsetof(InfiniFrameApplicationInitParams, NotificationRegistrationId) == sizeof(const char*)); +static_assert(offsetof(InfiniFrameApplicationInitParams, AppUserModelId) == sizeof(const char*) * 2); +static_assert(offsetof(InfiniFrameApplicationInitParams, DefaultNotificationIcon) == sizeof(const char*) * 3); +static_assert(offsetof(InfiniFrameApplicationInitParams, StructSize) == sizeof(const char*) * 4); +static_assert(offsetof(InfiniFrameApplicationInitParams, StructSize) + sizeof(int) <= sizeof(InfiniFrameApplicationInitParams)); +static_assert(offsetof(InfiniFrameWindowInitParams, StartString) == 0); +static_assert(offsetof(InfiniFrameWindowInitParams, StartUrl) > offsetof(InfiniFrameWindowInitParams, StartString)); +static_assert(offsetof(InfiniFrameWindowInitParams, CustomSchemeNames) > offsetof(InfiniFrameWindowInitParams, DebugEventHandler)); +static_assert(offsetof(InfiniFrameWindowInitParams, DragDropEnabled) > offsetof(InfiniFrameWindowInitParams, DragDropHandler)); +static_assert(offsetof(InfiniFrameWindowInitParams, BackgroundColorR) > offsetof(InfiniFrameWindowInitParams, NotificationsEnabled)); +static_assert(offsetof(InfiniFrameWindowInitParams, MenuBarJson) > offsetof(InfiniFrameWindowInitParams, BackgroundColorA)); +static_assert(offsetof(InfiniFrameWindowInitParams, StructSize) > offsetof(InfiniFrameWindowInitParams, MenuBarJson)); +static_assert(offsetof(InfiniFrameWindowInitParams, StructSize) + sizeof(int) <= sizeof(InfiniFrameWindowInitParams)); +static_assert(offsetof(CustomSchemeResponse, StructSize) == 0); diff --git a/src/InfiniFrame.NativeBridge/native-clean.ps1 b/src/InfiniFrame.NativeBridge/native-clean.ps1 index a503e7e97..e25f234fc 100644 --- a/src/InfiniFrame.NativeBridge/native-clean.ps1 +++ b/src/InfiniFrame.NativeBridge/native-clean.ps1 @@ -6,10 +6,6 @@ Remove-Item -Recurse -Force "$RootDir/build" -ErrorAction SilentlyContinue Remove-Item -Recurse -Force "$RootDir/artifacts" -ErrorAction SilentlyContinue Remove-Item -Recurse -Force "$RootDir/Native/build" -ErrorAction SilentlyContinue Remove-Item -Recurse -Force "$RootDir/Native/build-clang-tidy" -ErrorAction SilentlyContinue -Remove-Item -Recurse -Force "$RootDir/Native/cmake-build-debug-windows" -ErrorAction SilentlyContinue -Remove-Item -Recurse -Force "$RootDir/Native/cmake-build-debug-linux" -ErrorAction SilentlyContinue -Remove-Item -Recurse -Force "$RootDir/Native/cmake-build-release-windows" -ErrorAction SilentlyContinue -Remove-Item -Recurse -Force "$RootDir/Native/cmake-build-release-linux" -ErrorAction SilentlyContinue Remove-Item -Recurse -Force "$RootDir/Native/packages" -ErrorAction SilentlyContinue -Write-Host "Native clean complete." \ No newline at end of file +Write-Host "Native clean complete." diff --git a/src/InfiniFrame.Security/InfiniFrame.Security.csproj b/src/InfiniFrame.Security/InfiniFrame.Security.csproj new file mode 100644 index 000000000..6ecd178b9 --- /dev/null +++ b/src/InfiniFrame.Security/InfiniFrame.Security.csproj @@ -0,0 +1,10 @@ + + + InfiniLore.InfiniFrame.Security + InfiniFrame URI security policies, builders, registries, and extensions. + + + + + + diff --git a/src/InfiniFrame/Security/InfiniFrameUriSecurityPolicy.cs b/src/InfiniFrame.Security/InfiniFrameUriSecurityPolicy.cs similarity index 100% rename from src/InfiniFrame/Security/InfiniFrameUriSecurityPolicy.cs rename to src/InfiniFrame.Security/InfiniFrameUriSecurityPolicy.cs diff --git a/src/InfiniFrame/Security/InfiniFrameUriSecurityPolicyBuilder.cs b/src/InfiniFrame.Security/InfiniFrameUriSecurityPolicyBuilder.cs similarity index 100% rename from src/InfiniFrame/Security/InfiniFrameUriSecurityPolicyBuilder.cs rename to src/InfiniFrame.Security/InfiniFrameUriSecurityPolicyBuilder.cs diff --git a/src/InfiniFrame/Security/InfiniFrameUriSecurityPolicyBuilderExtensions.cs b/src/InfiniFrame.Security/InfiniFrameUriSecurityPolicyBuilderExtensions.cs similarity index 100% rename from src/InfiniFrame/Security/InfiniFrameUriSecurityPolicyBuilderExtensions.cs rename to src/InfiniFrame.Security/InfiniFrameUriSecurityPolicyBuilderExtensions.cs diff --git a/src/InfiniFrame/Security/InfiniFrameUriSecurityPolicyRegistry.cs b/src/InfiniFrame.Security/InfiniFrameUriSecurityPolicyRegistry.cs similarity index 100% rename from src/InfiniFrame/Security/InfiniFrameUriSecurityPolicyRegistry.cs rename to src/InfiniFrame.Security/InfiniFrameUriSecurityPolicyRegistry.cs diff --git a/src/InfiniFrame.Shared/Application/IInfiniFrameApplication.cs b/src/InfiniFrame.Shared/Application/IInfiniFrameApplication.cs new file mode 100644 index 000000000..eabe1e6ca --- /dev/null +++ b/src/InfiniFrame.Shared/Application/IInfiniFrameApplication.cs @@ -0,0 +1,43 @@ +// --------------------------------------------------------------------------------------------------------------------- +// Imports +// --------------------------------------------------------------------------------------------------------------------- +namespace InfiniFrame; +// --------------------------------------------------------------------------------------------------------------------- +// Code +// --------------------------------------------------------------------------------------------------------------------- +/// +/// Owns the windows registered for an InfiniFrame application. +/// +public interface IInfiniFrameApplication : IDisposable, IAsyncDisposable { + Guid Id { get; } + IntPtr ApplicationHandle { get; } + bool IsShutdownRequested { get; } + event Action? WindowCreated; + event Action? WindowDestroyed; + /// Registers an unnamed window to be built when the application runs. + void RegisterWindow(Action configure); + + /// Registers a named window to be built when the application runs. + void RegisterWindow(string id, Action configure); + + /// Gets a built window by its application-local identifier. + IInfiniFrameWindow GetWindow(string id); + + /// Tries to get a built window by its application-local identifier. + IInfiniFrameWindow? TryGetWindow(string id); + + /// Gets the windows built by this application. + IReadOnlyList Windows { get; } + + /// Runs the application until its windows have closed. + void Run(); + + /// Runs the application asynchronously until its windows have closed. + Task RunAsync(CancellationToken ct = default); + + /// Requests all owned windows to close. + void Shutdown(); + + /// Requests all owned windows to close. + void CloseAll(); +} diff --git a/src/InfiniFrame.Shared/BlazorWebView/IInfiniFrameBlazorApp.cs b/src/InfiniFrame.Shared/BlazorWebView/IInfiniFrameBlazorApp.cs deleted file mode 100644 index 45513878b..000000000 --- a/src/InfiniFrame.Shared/BlazorWebView/IInfiniFrameBlazorApp.cs +++ /dev/null @@ -1,23 +0,0 @@ -// --------------------------------------------------------------------------------------------------------------------- -// Imports -// --------------------------------------------------------------------------------------------------------------------- -namespace InfiniFrame.BlazorWebView; -// --------------------------------------------------------------------------------------------------------------------- -// Code -// --------------------------------------------------------------------------------------------------------------------- -/// -/// Represents a running Blazor application hosted in an InfiniFrame native window. -/// -public interface IInfiniFrameBlazorApp : IAsyncDisposable { - /// - /// Asynchronously runs the Blazor application and waits for the window to close. - /// - /// A cancellation token to cancel the operation. - /// A task that represents the asynchronous operation. - Task RunAsync(CancellationToken ct = default); - - /// - /// Runs the Blazor application synchronously, blocking until the window closes. - /// - void Run(); -} diff --git a/src/InfiniFrame.Shared/BlazorWebView/IInfiniFrameBlazorAppBuilder.cs b/src/InfiniFrame.Shared/BlazorWebView/IInfiniFrameBlazorAppBuilder.cs deleted file mode 100644 index 5373952bb..000000000 --- a/src/InfiniFrame.Shared/BlazorWebView/IInfiniFrameBlazorAppBuilder.cs +++ /dev/null @@ -1,28 +0,0 @@ -// --------------------------------------------------------------------------------------------------------------------- -// Imports -// --------------------------------------------------------------------------------------------------------------------- -using Microsoft.Extensions.DependencyInjection; - -namespace InfiniFrame.BlazorWebView; -// --------------------------------------------------------------------------------------------------------------------- -// Code -// --------------------------------------------------------------------------------------------------------------------- -/// -/// Defines the builder for creating a Blazor application hosted in an InfiniFrame native window. -/// -public interface IInfiniFrameBlazorAppBuilder { - /// - /// Gets the list of root components to be added to the Blazor application. - /// - IInfiniFrameRootComponentList RootComponents { get; } - - /// - /// Gets the service collection used to configure application services. - /// - IServiceCollection Services { get; } - - /// - /// Gets the window builder used to configure the application window. - /// - IInfiniFrameWindowBuilder WindowBuilder { get; } -} diff --git a/src/InfiniFrame.Shared/BlazorWebView/IInfiniFrameWebViewManager.cs b/src/InfiniFrame.Shared/BlazorWebView/IInfiniFrameWebViewManager.cs index 754defae1..56f715572 100644 --- a/src/InfiniFrame.Shared/BlazorWebView/IInfiniFrameWebViewManager.cs +++ b/src/InfiniFrame.Shared/BlazorWebView/IInfiniFrameWebViewManager.cs @@ -46,6 +46,14 @@ public interface IInfiniFrameWebViewManager { /// A task that returns true if the work item was dispatched successfully; otherwise, false. Task TryDispatchAsync(Action workItem); + /// + /// Handles a web message received from a native window. + /// + /// The native window that received the message. + /// The message payload. + /// The message origin, if supplied by the native web view. + void HandleWebMessage(IInfiniFrameWindow window, string message, string? origin); + /// /// Handles a web request from the native window, returning the response stream and content type. /// diff --git a/src/InfiniFrame.Shared/InfiniFrame.Shared.csproj b/src/InfiniFrame.Shared/InfiniFrame.Shared.csproj index 73737fae4..80a6935a1 100644 --- a/src/InfiniFrame.Shared/InfiniFrame.Shared.csproj +++ b/src/InfiniFrame.Shared/InfiniFrame.Shared.csproj @@ -2,16 +2,9 @@ InfiniFrame InfiniLore.InfiniFrame.Shared - Library - true Shared interfaces, types, enums, delegates, and native interop layer for InfiniFrame. Contains the feature system contracts used by the core and integration packages. - - - - - diff --git a/src/InfiniFrame.Shared/InfiniFrame.Shared.csproj.DotSettings b/src/InfiniFrame.Shared/InfiniFrame.Shared.csproj.DotSettings index 4995c6b3a..b7e3f6b2a 100644 --- a/src/InfiniFrame.Shared/InfiniFrame.Shared.csproj.DotSettings +++ b/src/InfiniFrame.Shared/InfiniFrame.Shared.csproj.DotSettings @@ -1,73 +1,38 @@ - - True - True + + True + True + True True - True - True - True + True + True + True True - False - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True + False + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True diff --git a/src/InfiniFrame.Shared/WebServer/IInfiniFrameWebApplicationBuilder.cs b/src/InfiniFrame.Shared/WebServer/IInfiniFrameWebApplicationBuilder.cs deleted file mode 100644 index 716334e4e..000000000 --- a/src/InfiniFrame.Shared/WebServer/IInfiniFrameWebApplicationBuilder.cs +++ /dev/null @@ -1,29 +0,0 @@ -// --------------------------------------------------------------------------------------------------------------------- -// Imports -// --------------------------------------------------------------------------------------------------------------------- -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; - -namespace InfiniFrame.WebServer; -// --------------------------------------------------------------------------------------------------------------------- -// Code -// --------------------------------------------------------------------------------------------------------------------- -/// -/// Defines the builder for creating an InfiniFrame web application with a native window. -/// -public interface IInfiniFrameWebApplicationBuilder { - /// - /// Gets the underlying used to configure the ASP.NET Core application. - /// - WebApplicationBuilder WebApp { get; } - - /// - /// Gets the window builder used to configure the application window. - /// - IInfiniFrameWindowBuilder WindowBuilder { get; } - - /// - /// Gets the service collection used to configure application services. - /// - IServiceCollection Services { get; } -} diff --git a/src/InfiniFrame.Shared/Window/Builder/IInfiniFrameWindowBuilderConfiguration.cs b/src/InfiniFrame.Shared/Window/Builder/IInfiniFrameWindowBuilderConfiguration.cs index 94ddac66e..6d5c442d6 100644 --- a/src/InfiniFrame.Shared/Window/Builder/IInfiniFrameWindowBuilderConfiguration.cs +++ b/src/InfiniFrame.Shared/Window/Builder/IInfiniFrameWindowBuilderConfiguration.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; namespace InfiniFrame; // --------------------------------------------------------------------------------------------------------------------- @@ -25,5 +25,5 @@ public interface IInfiniFrameWindowBuilderConfiguration { /// Applies the builder configuration values to the native parameters. /// /// The native parameters to update. - void ApplyToNativeParameters(ref InfiniFrameNativeParameters parameters); + void ApplyToNativeParameters(ref InfiniFrameNativeWindowParameters parameters); } diff --git a/src/InfiniFrame.Shared/Window/Builder/IInfiniFrameWindowBuilderFeature.cs b/src/InfiniFrame.Shared/Window/Builder/IInfiniFrameWindowBuilderFeature.cs index 7ea9d53c0..ee3eccfe9 100644 --- a/src/InfiniFrame.Shared/Window/Builder/IInfiniFrameWindowBuilderFeature.cs +++ b/src/InfiniFrame.Shared/Window/Builder/IInfiniFrameWindowBuilderFeature.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; namespace InfiniFrame; // --------------------------------------------------------------------------------------------------------------------- @@ -15,5 +15,5 @@ public interface IInfiniFrameWindowBuilderFeature { /// Applies the feature's configuration values to the native parameters. /// /// The native parameters to update. - internal void ApplyToNativeParameters(ref InfiniFrameNativeParameters parameters); + internal void ApplyToNativeParameters(ref InfiniFrameNativeWindowParameters parameters); } diff --git a/src/InfiniFrame.Shared/Window/Builder/IInfiniFrameWindowBuilderFeatures.cs b/src/InfiniFrame.Shared/Window/Builder/IInfiniFrameWindowBuilderFeatures.cs index d85f34fc6..4cfd5d32b 100644 --- a/src/InfiniFrame.Shared/Window/Builder/IInfiniFrameWindowBuilderFeatures.cs +++ b/src/InfiniFrame.Shared/Window/Builder/IInfiniFrameWindowBuilderFeatures.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; namespace InfiniFrame; // --------------------------------------------------------------------------------------------------------------------- @@ -65,5 +65,5 @@ public interface IInfiniFrameWindowBuilderFeatures { /// Applies all feature builder configurations to the native parameters. /// /// The native parameters to update. - internal void ApplyToNativeParameters(ref InfiniFrameNativeParameters parameters); + internal void ApplyToNativeParameters(ref InfiniFrameNativeWindowParameters parameters); } diff --git a/src/InfiniFrame.Shared/Window/Events/IInfiniFrameEvents.cs b/src/InfiniFrame.Shared/Window/Events/IInfiniFrameEvents.cs index 61db3e109..7eef8d60b 100644 --- a/src/InfiniFrame.Shared/Window/Events/IInfiniFrameEvents.cs +++ b/src/InfiniFrame.Shared/Window/Events/IInfiniFrameEvents.cs @@ -2,7 +2,7 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame.NativeBridge.Delegates; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; namespace InfiniFrame; // --------------------------------------------------------------------------------------------------------------------- @@ -112,7 +112,7 @@ public interface IInfiniFrameEvents : IHasInfiniFrameEventsStore { /// Assigns the event callbacks to the native parameters structure. /// /// The native parameters to assign callbacks to. - internal void AssignToNativeParameters(ref InfiniFrameNativeParameters nativeParameters); + internal void AssignToNativeParameters(ref InfiniFrameNativeWindowParameters nativeParameters); /// /// Assigns the window reference to this events instance. diff --git a/src/InfiniFrame.Shared/Window/Features/Browser/IBrowserInfiniFrameWindowBuilderFeature.cs b/src/InfiniFrame.Shared/Window/Features/Browser/IBrowserInfiniFrameWindowBuilderFeature.cs index 123a622a9..6971fdbe1 100644 --- a/src/InfiniFrame.Shared/Window/Features/Browser/IBrowserInfiniFrameWindowBuilderFeature.cs +++ b/src/InfiniFrame.Shared/Window/Features/Browser/IBrowserInfiniFrameWindowBuilderFeature.cs @@ -82,7 +82,6 @@ public interface IBrowserInfiniFrameWindowBuilderFeature : IInfiniFrameWindowBui /// /// Gets the fixed-version WebView2 runtime path used on Windows. /// - string? WebView2RuntimePath { get; } /// /// Enables or disables the context menu. @@ -177,10 +176,4 @@ public interface IBrowserInfiniFrameWindowBuilderFeature : IInfiniFrameWindowBui /// /// The temporary files path. void SetTemporaryFilesPath(string parameters); - - /// - /// Sets the fixed-version WebView2 runtime path used when creating the window on Windows. - /// - /// The path to the extracted WebView2 runtime directory. - void SetWebView2RuntimePath(string path); } diff --git a/src/InfiniFrame.Shared/Window/Features/Browser/IBrowserInfiniFrameWindowBuilderFeatureExtensions.cs b/src/InfiniFrame.Shared/Window/Features/Browser/IBrowserInfiniFrameWindowBuilderFeatureExtensions.cs index 49b6b2001..10d26a10e 100644 --- a/src/InfiniFrame.Shared/Window/Features/Browser/IBrowserInfiniFrameWindowBuilderFeatureExtensions.cs +++ b/src/InfiniFrame.Shared/Window/Features/Browser/IBrowserInfiniFrameWindowBuilderFeatureExtensions.cs @@ -172,15 +172,4 @@ public static IInfiniFrameWindowBuilder SetTemporaryFilesPath(this IInfiniFrameW builder.Features.Browser.SetTemporaryFilesPath(parameters); return builder; } - - /// - /// Sets the fixed-version WebView2 runtime path used when creating the window on Windows. - /// - /// The builder instance. - /// The path to the extracted WebView2 runtime directory. - /// The builder instance for chaining. - public static IInfiniFrameWindowBuilder SetWebView2RuntimePath(this IInfiniFrameWindowBuilder builder, string path) { - builder.Features.Browser.SetWebView2RuntimePath(path); - return builder; - } } diff --git a/src/InfiniFrame.Shared/Window/Features/Debugging/IDebuggingInfiniFrameWindowBuilderFeature.cs b/src/InfiniFrame.Shared/Window/Features/Debugging/IDebuggingInfiniFrameWindowBuilderFeature.cs index f55e55c3b..dd30588a8 100644 --- a/src/InfiniFrame.Shared/Window/Features/Debugging/IDebuggingInfiniFrameWindowBuilderFeature.cs +++ b/src/InfiniFrame.Shared/Window/Features/Debugging/IDebuggingInfiniFrameWindowBuilderFeature.cs @@ -2,7 +2,7 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using System.Runtime.Versioning; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; namespace InfiniFrame; // --------------------------------------------------------------------------------------------------------------------- @@ -61,5 +61,5 @@ public interface IDebuggingInfiniFrameWindowBuilderFeature { [SupportedOSPlatform("linux")] IDebuggingInfiniFrameWindowBuilderFeature SetRemoteDebuggingPort(int port); - internal void ApplyToNativeParameters(ref InfiniFrameNativeParameters parameters); + internal void ApplyToNativeParameters(ref InfiniFrameNativeWindowParameters parameters); } diff --git a/src/InfiniFrame.Shared/Window/Features/Decorations/IDecorationsInfiniFrameWindowBuilderFeature.cs b/src/InfiniFrame.Shared/Window/Features/Decorations/IDecorationsInfiniFrameWindowBuilderFeature.cs index 27f16d3b1..afd28fa26 100644 --- a/src/InfiniFrame.Shared/Window/Features/Decorations/IDecorationsInfiniFrameWindowBuilderFeature.cs +++ b/src/InfiniFrame.Shared/Window/Features/Decorations/IDecorationsInfiniFrameWindowBuilderFeature.cs @@ -34,11 +34,6 @@ public interface IDecorationsInfiniFrameWindowBuilderFeature : IInfiniFrameWindo /// string? IconFilePath { get; } - /// - /// Gets the explicit Windows application user model ID used for taskbar grouping and identity. - /// - string? WindowsAppUserModelId { get; } - /// /// Gets whether Linux window title length is limited. /// @@ -74,13 +69,6 @@ public interface IDecorationsInfiniFrameWindowBuilderFeature : IInfiniFrameWindo /// The path to the icon file. void SetIconFile(string iconFilePath); - /// - /// Sets the explicit Windows application user model ID for the current process. - /// All windows in a process should use the same ID. - /// - /// The application user model ID, or null to use Windows' default identity. - void SetWindowsAppUserModelId(string? appUserModelId); - /// /// Sets whether the Linux window title length should be limited. /// diff --git a/src/InfiniFrame.Shared/Window/Features/Decorations/IDecorationsInfiniFrameWindowBuilderFeatureExtensions.cs b/src/InfiniFrame.Shared/Window/Features/Decorations/IDecorationsInfiniFrameWindowBuilderFeatureExtensions.cs index f31c3f385..68e7d84e3 100644 --- a/src/InfiniFrame.Shared/Window/Features/Decorations/IDecorationsInfiniFrameWindowBuilderFeatureExtensions.cs +++ b/src/InfiniFrame.Shared/Window/Features/Decorations/IDecorationsInfiniFrameWindowBuilderFeatureExtensions.cs @@ -64,21 +64,6 @@ public static IInfiniFrameWindowBuilder SetIconFile(this IInfiniFrameWindowBuild return builder; } - /// - /// Sets the explicit Windows application user model ID used for taskbar grouping and application identity. - /// All windows in a process should use the same ID. - /// - /// The builder instance. - /// The application user model ID, or null to use Windows' default identity. - /// The builder instance for chaining. - public static IInfiniFrameWindowBuilder SetWindowsAppUserModelId( - this IInfiniFrameWindowBuilder builder, - string? appUserModelId - ) { - builder.Features.Decorations.SetWindowsAppUserModelId(appUserModelId); - return builder; - } - /// /// Sets whether the Linux window title length should be limited. /// diff --git a/src/InfiniFrame.Shared/Window/Features/Lifecycle/ILifecycleInfiniFrameWindowFeature.cs b/src/InfiniFrame.Shared/Window/Features/Lifecycle/ILifecycleInfiniFrameWindowFeature.cs index 0dcb2e27c..5688c8fee 100644 --- a/src/InfiniFrame.Shared/Window/Features/Lifecycle/ILifecycleInfiniFrameWindowFeature.cs +++ b/src/InfiniFrame.Shared/Window/Features/Lifecycle/ILifecycleInfiniFrameWindowFeature.cs @@ -68,6 +68,7 @@ public interface ILifecycleInfiniFrameWindowFeature { internal void MarkAsClosed(); internal void MarkClosedCallbacksDelivered(); internal void MarkCloseRejected(); + internal void CompleteTeardownAfterNativeLoop(); /// /// Frees the native window handle. Must be called outside of native signal handlers. diff --git a/src/InfiniFrame.Shared/Window/IInfiniFrameWindowConfiguration.cs b/src/InfiniFrame.Shared/Window/IInfiniFrameWindowConfiguration.cs index b36b171ff..4260b62cd 100644 --- a/src/InfiniFrame.Shared/Window/IInfiniFrameWindowConfiguration.cs +++ b/src/InfiniFrame.Shared/Window/IInfiniFrameWindowConfiguration.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; namespace InfiniFrame; // --------------------------------------------------------------------------------------------------------------------- @@ -15,7 +15,7 @@ public interface IInfiniFrameWindowConfiguration { /// /// Gets the native parameters used when starting the window. /// - InfiniFrameNativeParameters StartupParameters { get; } + InfiniFrameNativeWindowParameters StartupParameters { get; } /// /// Gets or sets the parent window of this window. @@ -31,5 +31,5 @@ public interface IInfiniFrameWindowConfiguration { /// Assigns the native parameters to this configuration. /// /// The native parameters to assign. - internal void AssignNativeParameters(InfiniFrameNativeParameters nativeParameters); + internal void AssignNativeParameters(InfiniFrameNativeWindowParameters nativeParameters); } diff --git a/src/InfiniFrame.SingleFile/InfiniFrame.SingleFile.csproj b/src/InfiniFrame.SingleFile/InfiniFrame.SingleFile.csproj index 1e7da2172..d61654674 100644 --- a/src/InfiniFrame.SingleFile/InfiniFrame.SingleFile.csproj +++ b/src/InfiniFrame.SingleFile/InfiniFrame.SingleFile.csproj @@ -15,8 +15,9 @@ + - + diff --git a/src/InfiniFrame.SingleFile/InfiniFrame.SingleFile.targets b/src/InfiniFrame.SingleFile/InfiniFrame.SingleFile.targets index 6b77f250e..36481c0d7 100644 --- a/src/InfiniFrame.SingleFile/InfiniFrame.SingleFile.targets +++ b/src/InfiniFrame.SingleFile/InfiniFrame.SingleFile.targets @@ -91,7 +91,8 @@ - <_InfiniFramePackPublishDir>$([System.IO.Path]::Combine('$(MSBuildProjectDirectory)', '$(OutputPath)', 'publish')) + <_InfiniFramePackPublishDir Condition="'$(PublishDir)' != ''">$(PublishDir) + <_InfiniFramePackPublishDir Condition="'$(_InfiniFramePackPublishDir)' == ''">$([System.IO.Path]::Combine('$(MSBuildProjectDirectory)', '$(OutputPath)', 'publish')) <_InfiniFramePackSidecarFile Include="$(_InfiniFramePackPublishDir)\*.staticwebassets.endpoints.json"/> @@ -195,7 +196,8 @@ - <_InfiniFramePackPublishDir>$([System.IO.Path]::Combine('$(MSBuildProjectDirectory)', '$(OutputPath)', 'publish')) + <_InfiniFramePackPublishDir Condition="'$(PublishDir)' != ''">$(PublishDir) + <_InfiniFramePackPublishDir Condition="'$(_InfiniFramePackPublishDir)' == ''">$([System.IO.Path]::Combine('$(MSBuildProjectDirectory)', '$(OutputPath)', 'publish')) diff --git a/src/InfiniFrame.SingleFile/InfiniFrameSingleFile.cs b/src/InfiniFrame.SingleFile/InfiniFrameSingleFile.cs index ec3e766d5..0b07619bd 100644 --- a/src/InfiniFrame.SingleFile/InfiniFrameSingleFile.cs +++ b/src/InfiniFrame.SingleFile/InfiniFrameSingleFile.cs @@ -4,7 +4,6 @@ using System.Runtime.InteropServices; using InfiniFrame.BlazorWebView; using InfiniFrame.BlazorWebView.FileProviders; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; namespace InfiniFrame.SingleFile; @@ -46,12 +45,12 @@ public static void AddSingleFileRequirements(this IInfiniFrameWindowBuilder buil /// Registers the single-file for Blazor app integration. /// /// The Blazor app builder. - public static void AddSingleFileRequirements(this IInfiniFrameBlazorAppBuilder builder) { + public static void AddSingleFileRequirements(this InfiniFrameBlazorWebViewConfiguration builder) { if (!InfiniFramePackMode.IsActive) return; string baseDirectory = AppDomain.CurrentDomain.BaseDirectory; if (!SingleFileModeFileProvider.TryCreate(baseDirectory, out IFileProvider? fileProvider)) return; - builder.Services.AddSingleton(fileProvider); + builder.AddSingleFileProvider(fileProvider); } } diff --git a/src/InfiniFrame.SingleFile/InfiniFrameSingleFileBootstrap.cs b/src/InfiniFrame.SingleFile/InfiniFrameSingleFileBootstrap.cs index 944d99d40..49f135bab 100644 --- a/src/InfiniFrame.SingleFile/InfiniFrameSingleFileBootstrap.cs +++ b/src/InfiniFrame.SingleFile/InfiniFrameSingleFileBootstrap.cs @@ -52,7 +52,7 @@ internal static void Initialize() { bool initialized = false; try { string[] requiredFiles = GetNativeFileNamesForCurrentPlatform(); - bool hasResources = requiredFiles.Any(fileName => { + bool hasResources = requiredFiles.All(fileName => { string resourceName = $"{entryAssembly.GetName().Name}.native.{rid}.{fileName}"; return entryAssembly.GetManifestResourceStream(resourceName) is not null; }); @@ -118,7 +118,8 @@ private static void ExtractEmbeddedNative(Assembly assembly, string rid, IReadOn foreach (string fileName in fileNames) { string resourceName = $"{assembly.GetName().Name}.native.{rid}.{fileName}"; using Stream? resourceStream = assembly.GetManifestResourceStream(resourceName); - if (resourceStream is null) continue; + if (resourceStream is null) + throw new InvalidDataException($"Required single-file native resource '{resourceName}' was not found."); string destinationPath = Path.Join(_nativeDir!, fileName); diff --git a/src/InfiniFrame/StaticAssets/DisposableCompositeFileProvider.cs b/src/InfiniFrame.StaticAssets/DisposableCompositeFileProvider.cs similarity index 100% rename from src/InfiniFrame/StaticAssets/DisposableCompositeFileProvider.cs rename to src/InfiniFrame.StaticAssets/DisposableCompositeFileProvider.cs diff --git a/src/InfiniFrame/StaticAssets/FileProviderFactory.cs b/src/InfiniFrame.StaticAssets/FileProviderFactory.cs similarity index 100% rename from src/InfiniFrame/StaticAssets/FileProviderFactory.cs rename to src/InfiniFrame.StaticAssets/FileProviderFactory.cs diff --git a/src/InfiniFrame.StaticAssets/InfiniFrame.StaticAssets.csproj b/src/InfiniFrame.StaticAssets/InfiniFrame.StaticAssets.csproj new file mode 100644 index 000000000..71df08683 --- /dev/null +++ b/src/InfiniFrame.StaticAssets/InfiniFrame.StaticAssets.csproj @@ -0,0 +1,15 @@ + + + InfiniLore.InfiniFrame.StaticAssets + InfiniFrame static asset providers, models, scheme handlers, and extensions. + + + + + + + + + + + diff --git a/src/InfiniFrame/StaticAssets/InfiniFrameStaticAssets.cs b/src/InfiniFrame.StaticAssets/InfiniFrameStaticAssets.cs similarity index 100% rename from src/InfiniFrame/StaticAssets/InfiniFrameStaticAssets.cs rename to src/InfiniFrame.StaticAssets/InfiniFrameStaticAssets.cs diff --git a/src/InfiniFrame/StaticAssets/InfiniWindowBuilderStaticAssetExtensions.cs b/src/InfiniFrame.StaticAssets/InfiniWindowBuilderStaticAssetExtensions.cs similarity index 100% rename from src/InfiniFrame/StaticAssets/InfiniWindowBuilderStaticAssetExtensions.cs rename to src/InfiniFrame.StaticAssets/InfiniWindowBuilderStaticAssetExtensions.cs diff --git a/src/InfiniFrame/StaticAssets/StaticAssetSchemeHandler.cs b/src/InfiniFrame.StaticAssets/StaticAssetSchemeHandler.cs similarity index 100% rename from src/InfiniFrame/StaticAssets/StaticAssetSchemeHandler.cs rename to src/InfiniFrame.StaticAssets/StaticAssetSchemeHandler.cs diff --git a/src/InfiniFrame.WebServer/InfiniFrame.WebServer.csproj b/src/InfiniFrame.WebServer/InfiniFrame.WebServer.csproj index 09e2c9003..5b81fe90d 100644 --- a/src/InfiniFrame.WebServer/InfiniFrame.WebServer.csproj +++ b/src/InfiniFrame.WebServer/InfiniFrame.WebServer.csproj @@ -20,7 +20,8 @@ - + + diff --git a/src/InfiniFrame.WebServer/InfiniFrameApplicationWebServerExtensions.cs b/src/InfiniFrame.WebServer/InfiniFrameApplicationWebServerExtensions.cs new file mode 100644 index 000000000..6445ea354 --- /dev/null +++ b/src/InfiniFrame.WebServer/InfiniFrameApplicationWebServerExtensions.cs @@ -0,0 +1,106 @@ +// --------------------------------------------------------------------------------------------------------------------- +// Imports +// --------------------------------------------------------------------------------------------------------------------- +using InfiniFrame.Application; +using InfiniFrame.Security; +using InfiniFrame.Window.Features.WebMessaging.Handlers; +using Microsoft.AspNetCore.Hosting.Server.Features; + +namespace InfiniFrame.WebServer; +// --------------------------------------------------------------------------------------------------------------------- +// Code +// --------------------------------------------------------------------------------------------------------------------- +public static class InfiniFrameApplicationWebServerExtensions { + public static InfiniFrameApplicationBuilder UseWebServer( + this InfiniFrameApplicationBuilder builder, + Action configure + ) => builder.UseWebServer(configure, []); + + public static InfiniFrameApplicationBuilder UseWebServer( + this InfiniFrameApplicationBuilder builder, + string windowId, + Action configure + ) => builder.UseWebServer(configure, windowId); + + public static InfiniFrameApplicationBuilder UseWebServer( + this InfiniFrameApplicationBuilder builder, + IEnumerable windowIds, + Action configure + ) { + ArgumentNullException.ThrowIfNull(windowIds); + return builder.UseWebServer(configure, windowIds.ToArray()); + } + + public static InfiniFrameApplicationBuilder UseWebServer( + this InfiniFrameApplicationBuilder builder, + Action configure, + params string[] windowIds + ) { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(configure); + + var configuration = new InfiniFrameWebServerConfiguration( + WebApplication.CreateBuilder(builder.Args), + builder.Services + ); + configure(configuration); + + builder.AddIntegration(application => { + application.ValidateWindowIntegrationTargets(windowIds, "WebServer"); + WebApplication webApplication = configuration.BuildApplication(); + int serverCleanupStarted = 0; + + async Task StopServerAsync() { + if (Interlocked.Exchange(ref serverCleanupStarted, 1) != 0) return; + try { + await webApplication.StopAsync(CancellationToken.None).ConfigureAwait(false); + } + finally { + await webApplication.DisposeAsync().ConfigureAwait(false); + } + } + + application.RegisterStartupAction(async () => { + try { + await webApplication.StartAsync().ConfigureAwait(false); + Uri address = ResolveStartedAddress(webApplication); + application.ApplyWindowIntegration(windowIds, "WebServer", configure: windowBuilder => { + windowBuilder.SetStartPageUrl(address.ToString()); + windowBuilder.RegisterGetWebMessageHandler(); + InfiniFrameUriSecurityPolicyRegistry.ConfigureForBuilder( + windowBuilder, + configure: policyBuilder => policyBuilder.AddTrustedOrigin(address) + ); + }); + } + catch { + await StopServerAsync().ConfigureAwait(false); + throw; + } + }); + + application.RegisterShutdownAction(StopServerAsync); + }); + return builder; + } + + private static Uri ResolveStartedAddress(WebApplication application) { + IEnumerable addresses = application.Services + .GetService()?.Features + .Get()?.Addresses + ?? application.Urls; + string? address = addresses.FirstOrDefault(candidate => + Uri.TryCreate(candidate, UriKind.Absolute, out Uri? uri) && uri.Port != 0); + if (address is null || !Uri.TryCreate(address, UriKind.Absolute, out Uri? result)) + throw new InvalidOperationException( + "The ASP.NET Core web server started but its bound address could not be resolved. " + + "Refusing to navigate a window to an unresolved port."); + + if (result.Host is "*" or "+" or "0.0.0.0" or "::") { + var loopback = new UriBuilder(result) { Host = "127.0.0.1" }; + return loopback.Uri; + } + + return result; + } +} diff --git a/src/InfiniFrame.WebServer/InfiniFrameWebApplication.cs b/src/InfiniFrame.WebServer/InfiniFrameWebApplication.cs deleted file mode 100644 index 988b49218..000000000 --- a/src/InfiniFrame.WebServer/InfiniFrameWebApplication.cs +++ /dev/null @@ -1,169 +0,0 @@ -// --------------------------------------------------------------------------------------------------------------------- -// Imports -// --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame.Utilities; - -namespace InfiniFrame.WebServer; -// --------------------------------------------------------------------------------------------------------------------- -// Code -// --------------------------------------------------------------------------------------------------------------------- -/// -/// Represents an InfiniFrame application that integrates an ASP.NET Core with an -/// , providing lifecycle management for both the web server and the native window. -/// -public class InfiniFrameWebApplication { - #if NET9_0_OR_GREATER - private readonly Lock _shutdownLock = new(); - #else - private readonly object _shutdownLock = new(); - #endif - private Task? _shutdownTask; - - /// Gets or sets the logger for the application. - public required ILogger Logger { get; init; } - /// Gets or sets the underlying ASP.NET Core . - public required WebApplication WebApp { get; init; } - /// Gets or sets the lazy factory for the associated window. - public required Lazy LazyWindow { private get; init; } - /// Gets the associated InfiniFrame window instance. - public IInfiniFrameWindow Window => LazyWindow.Value; - - // ----------------------------------------------------------------------------------------------------------------- - // Methods - // ----------------------------------------------------------------------------------------------------------------- - /// - /// Creates a new with default ASP.NET Core and InfiniFrame - /// window builder services. - /// - /// Command-line arguments passed to the ASP.NET Core host builder. - /// An for further configuration. - public static InfiniFrameWebApplicationBuilder CreateBuilder(params string[] args) - => new InfiniFrameWebApplicationBuilder { - WebApp = WebApplication.CreateBuilder(args), - WindowBuilder = InfiniFrameWindowBuilder.Create() - }.Initialize(); - - /// - /// Runs the web application and window, blocking until the window is closed. - /// - /// - /// This method uses synchronous-over-async patterns for ASP.NET Core host lifecycle - /// operations. It should only be called from threads without a SynchronizationContext - /// (e.g., console applications or the default thread pool). Prefer - /// for async contexts. - /// - public void Run() { - if (SynchronizationContext.Current is not null) { - throw new InvalidOperationException( - "Run() must be called from a thread without a SynchronizationContext to avoid deadlock during lifecycle operations. " + - "Use RunAsync() instead."); - } - - try { - // Wait until the host is accepting requests before creating the window. On Windows, - // WaitForClose owns the native message loop required by WebView2 initialization and - // navigation; WaitForCloseAsync only observes the closed signal. - WebApp.StartAsync().GetAwaiter().GetResult(); - Window.WaitForClose(); - } - finally { - try { - StopWebAppAsync(CancellationToken.None).GetAwaiter().GetResult(); - } - finally { - WebApp.DisposeAsync().AsTask().GetAwaiter().GetResult(); - } - } - } - - /// - /// Runs the web application and window asynchronously, waiting for the window to close before stopping the server. - /// - /// A cancellation token that can be used to cancel the operation. - /// A task that completes when the window has been closed and the web app has stopped. - public async Task RunAsync(CancellationToken ct = default) { - try { - await WebApp.StartAsync(ct).ConfigureAwait(false); - await Window.WaitForCloseAsync(ct).ConfigureAwait(false); - } - finally { - try { - await StopWebAppAsync(CancellationToken.None).ConfigureAwait(false); - } - finally { - await WebApp.DisposeAsync().ConfigureAwait(false); - } - } - } - - /// - /// Configures the application to automatically handle server shutdown when the associated - /// window is closing or a close request is initiated. This method registers event handlers - /// that trigger graceful stopping of the web application upon user-initiated window close actions. - /// By invoking this method, the application ensures the proper release of resources and - /// termination of processes tied to the web server during window closure. - /// - /// - /// Returns the current instance of to enable method chaining. - /// - public InfiniFrameWebApplication UseAutoServerClose() { - if (LazyWindow.IsValueCreated) { - Window.RegisterWindowClosingHandler((_, _) => ClosingHandler()); - Window.RegisterWindowClosingRequestedHandler(_ => ClosingHandler()); - return this; - } - - var builder = WebApp.Services.GetRequiredService(); - builder.RegisterWindowClosingHandler((_, _) => ClosingHandler()); - builder.RegisterWindowClosingRequestedHandler(_ => ClosingHandler()); - return this; - - WindowClosingResult ClosingHandler() { - // This runs inside a native UI callback. The shared shutdown task is observed by - // RunAsync/StopAsync, so starting it here does not block the UI thread or lose it. - _ = StopWebAppAsync(); - // return false else the window will not be closed (see old InfiniFrame code why) - return WindowClosingResult.Close; - } - } - - /// - /// Stops the web application and closes the associated application window. - /// This method ensures that both the server instance and the user interface - /// are gracefully terminated. - /// - public void Stop() { - StopAsync().GetAwaiter().GetResult(); - } - - /// - /// Stops the web application and closes the associated window asynchronously. - /// - /// A cancellation token that can be used to cancel the stop operation. - /// A task that completes when both the web app and window have been stopped. - public async Task StopAsync(CancellationToken ct = default) { - await StopWebAppAsync(ct).ConfigureAwait(false); - await Window.CloseAsync(ct).ConfigureAwait(false); - await Window.WaitForCloseAsync(ct).ConfigureAwait(false); - } - - private Task StopWebAppAsync(CancellationToken ct = default) { - Task shutdownTask; - lock (_shutdownLock) { - shutdownTask = _shutdownTask ??= StopWebAppCoreAsync(); - } - - return shutdownTask.WaitAsync(ct); - } - - private async Task StopWebAppCoreAsync() { - try { - // Cancellation only cancels an individual caller's wait. Once shutdown starts it - // must run to completion for all callers. - await WebApp.StopAsync(CancellationToken.None).ConfigureAwait(false); - } - catch (Exception e) when (ExceptionsUtility.IsNonFatalException(e)) { - Logger.LogError(e, "Error stopping web app"); - } - } -} diff --git a/src/InfiniFrame.WebServer/InfiniFrameWebApplicationBuilder.cs b/src/InfiniFrame.WebServer/InfiniFrameWebApplicationBuilder.cs deleted file mode 100644 index 6ec0d79db..000000000 --- a/src/InfiniFrame.WebServer/InfiniFrameWebApplicationBuilder.cs +++ /dev/null @@ -1,77 +0,0 @@ -// --------------------------------------------------------------------------------------------------------------------- -// Imports -// --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame.Security; -using Microsoft.Extensions.Logging.Abstractions; - -namespace InfiniFrame.WebServer; -// --------------------------------------------------------------------------------------------------------------------- -// Code -// --------------------------------------------------------------------------------------------------------------------- -/// -/// Builder for creating an ASP.NET Core web application with a native InfiniFrame window. -/// -public class InfiniFrameWebApplicationBuilder : IInfiniFrameWebApplicationBuilder { - /// - public required WebApplicationBuilder WebApp { get; init; } - /// - public required IInfiniFrameWindowBuilder WindowBuilder { get; init; } - - /// - public IServiceCollection Services => WebApp.Services; - - // ----------------------------------------------------------------------------------------------------------------- - // Methods - // ----------------------------------------------------------------------------------------------------------------- - internal InfiniFrameWebApplicationBuilder Initialize() { - Services - .AddInfiniFrame() - .AddSingleton(WindowBuilder) - .AddSingleton(static provider => provider.GetRequiredService().Build(provider)); - - WebApp.WebHost.UseStaticWebAssets(); - - // Prefer ASPNETCORE_URLS, then "urls" else it has to be set by the dev themselves - string? configuredUrls = WebApp.Configuration["ASPNETCORE_URLS"] - ?? WebApp.Configuration["urls"]; - - // If there are multiple URLs (e.g. "https://localhost:7210;http://localhost:5210"), picks the first - string? startUrl = configuredUrls? - .Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) - .FirstOrDefault(); - - if (startUrl is not null) WindowBuilder.SetStartPageUrl(startUrl); - - WindowBuilder.RegisterGetWebMessageHandler(); - - return this; - } - - /// - /// Builds the web application and native window, returning an . - /// - /// The built . - public InfiniFrameWebApplication Build() { - WebApplication webApp = WebApp.Build(); - - webApp.UseDefaultFiles(); - - string? configuredUrls = WebApp.Configuration["ASPNETCORE_URLS"] - ?? WebApp.Configuration["urls"]; - string? startUrl = configuredUrls? - .Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) - .FirstOrDefault(); - - if (startUrl is not null && Uri.TryCreate(startUrl, UriKind.Absolute, out Uri? baseUri)) { - InfiniFrameUriSecurityPolicyRegistry.ConfigureForBuilder( - WindowBuilder, - configure: policyBuilder => policyBuilder.AddTrustedOrigin(baseUri)); - } - - return new InfiniFrameWebApplication { - Logger = webApp.Services.GetService>() ?? NullLogger.Instance, - WebApp = webApp, - LazyWindow = new Lazy(() => webApp.Services.GetRequiredService()) - }; - } -} diff --git a/src/InfiniFrame.WebServer/InfiniFrameWebServerConfiguration.cs b/src/InfiniFrame.WebServer/InfiniFrameWebServerConfiguration.cs new file mode 100644 index 000000000..8356616d9 --- /dev/null +++ b/src/InfiniFrame.WebServer/InfiniFrameWebServerConfiguration.cs @@ -0,0 +1,55 @@ +// --------------------------------------------------------------------------------------------------------------------- +// Imports +// --------------------------------------------------------------------------------------------------------------------- +namespace InfiniFrame.WebServer; +// --------------------------------------------------------------------------------------------------------------------- +// Code +// --------------------------------------------------------------------------------------------------------------------- +public sealed class InfiniFrameWebServerConfiguration { + private readonly WebApplicationBuilder _webApplicationBuilder; + + internal InfiniFrameWebServerConfiguration(WebApplicationBuilder webApp, IServiceCollection services) { + _webApplicationBuilder = webApp; + RootServices = services; + } + + public IWebHostBuilder WebHost => _webApplicationBuilder.WebHost; + internal WebApplicationBuilder Builder => _webApplicationBuilder; + internal IServiceCollection RootServices { get; } + + private readonly List> _applicationConfiguration = []; + + public InfiniFrameWebServerConfiguration ConfigureWebApplication(Action configure) { + ArgumentNullException.ThrowIfNull(configure); + _applicationConfiguration.Add(configure); + return this; + } + + internal void Apply(WebApplication application) { + foreach (Action configure in _applicationConfiguration) + configure(application); + } + + internal void AddRootServices() { + foreach (ServiceDescriptor descriptor in RootServices) + _webApplicationBuilder.Services.Add(descriptor); + } + + internal WebApplication Build() => _webApplicationBuilder.Build(); + + internal WebApplication BuildApplication() { + AddRootServices(); + _webApplicationBuilder.WebHost.UseStaticWebAssets(); + WebApplication application = _webApplicationBuilder.Build(); + try { + application.UseDefaultFiles(); + application.UseStaticFiles(); + Apply(application); + return application; + } + catch { + application.DisposeAsync().AsTask().GetAwaiter().GetResult(); + throw; + } + } +} diff --git a/src/InfiniFrame.Window/Builder/InfiniFrameWindowBuilder.cs b/src/InfiniFrame.Window/Builder/InfiniFrameWindowBuilder.cs new file mode 100644 index 000000000..8854232c4 --- /dev/null +++ b/src/InfiniFrame.Window/Builder/InfiniFrameWindowBuilder.cs @@ -0,0 +1,122 @@ +// --------------------------------------------------------------------------------------------------------------------- +// Imports +// --------------------------------------------------------------------------------------------------------------------- +using FluentValidation; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Security; +using InfiniFrame.Window.Events; +using InfiniFrame.Window.Features.InstanceArbitration; +using Microsoft.Extensions.DependencyInjection; + +namespace InfiniFrame.Window.Builder; +// --------------------------------------------------------------------------------------------------------------------- +// Code +// --------------------------------------------------------------------------------------------------------------------- +/// +/// Default implementation of that collects configuration, +/// features, and event handlers to construct an . +/// +public class InfiniFrameWindowBuilder : IInfiniFrameWindowBuilder { + internal IntPtr ApplicationHandle { get; private set; } + + private IServiceCollection Services { get; } + /// + public IInfiniFrameWindowBuilderConfiguration Configuration { get; } = new InfiniFrameWindowBuilderConfiguration(); + /// + public IInfiniFrameWindowBuilderFeatures Features { get; } = new InfiniFrameWindowBuilderFeatures(); + /// + public IDebuggingInfiniFrameWindowBuilderFeature Debugging => Features.Debugging; + /// + public IInfiniFrameEventsStore EventsStore { get; } + + /// + public IInfiniFrameStaticAssets? StaticAssets { get; set; } + + public InfiniFrameWindowBuilder(IServiceCollection? collection = null, InfiniFrameEventsStore? events = null) { + EventsStore = events ?? new InfiniFrameEventsStore(); + Services = (collection ?? new ServiceCollection()) + .AddLogging() + .AddInfiniFrame(); + } + + internal void SetApplicationHandle(IntPtr handle) => ApplicationHandle = handle; + + // ----------------------------------------------------------------------------------------------------------------- + // Methods + // ----------------------------------------------------------------------------------------------------------------- + /// + public IInfiniFrameWindow Build(IServiceProvider? provider = null) { + bool ownsServiceProvider = provider is null; + IServiceProvider actualProvider = provider ?? Services.BuildServiceProvider(); + InfiniFrameWindow? window = null; + try { + var featureFactory = actualProvider.GetRequiredService(); + var validator = actualProvider.GetRequiredService>(); + + InfiniFrameNativeWindowParameters nativeParameters = CollectNativeParameters(); + validator.ValidateAndThrow(nativeParameters); + + // Instance arbitration check + IInstanceArbitrationInfiniFrameWindowBuilderFeature arbitration = Features.InstanceArbitration; + if (arbitration.Mode != InstanceArbitrationMode.Disabled + && !InstanceArbitration.TryAcquirePrimaryInstance(arbitration.MutexName)) { + throw new InstanceAlreadyRunningException(); + } + + window = actualProvider.GetRequiredService(); + window.AssignFeatures(featureFactory.Create(window, this)); + + window.Events.PopulateFromBuilderEventStore(EventsStore); + window.Events.AssignToNativeParameters(ref nativeParameters); + window.Events.AssignDefaultEventCallbacks(); + window.Events.AssignToWindow(window); + + window.Configuration.ParentWindow = Configuration.ParentWindow; + window.Configuration.AssignNativeParameters(nativeParameters); + + InfiniFrameUriSecurityPolicyRegistry.BindToWindow( + window, + InfiniFrameUriSecurityPolicyRegistry.GetForBuilder(this) + ); + + window.Features.Lifecycle.Initialize(); + if (window.Configuration.ParentWindow?.Configuration is InfiniFrameWindowConfiguration parentConfiguration) { + lock (parentConfiguration.ChildWindowsLock) { + if (!parentConfiguration.ChildWindowsInternal.Contains(window)) + parentConfiguration.ChildWindowsInternal.Add(window); + } + } + window.SetOwnsServiceProvider(ownsServiceProvider); + return window; + } + catch { + window?.Dispose(); + if (ownsServiceProvider) + (actualProvider as IDisposable)?.Dispose(); + throw; + } + + } + + // ----------------------------------------------------------------------------------------------------------------- + // Constructors + // ----------------------------------------------------------------------------------------------------------------- + /// + /// Creates a new with optional DI and event store overrides. + /// + /// Optional service collection. If null, a default collection with logging and InfiniFrame core services is created. + /// Optional pre-configured event store. If null, a new empty store is created. + /// A configured ready for feature configuration. + internal static InfiniFrameWindowBuilder Create(IServiceCollection? collection = null, InfiniFrameEventsStore? events = null) + => new(collection, events); + + internal InfiniFrameNativeWindowParameters CollectNativeParameters() { + var parameters = new InfiniFrameNativeWindowParameters(); + + Configuration.ApplyToNativeParameters(ref parameters); + Features.ApplyToNativeParameters(ref parameters); + parameters.ApplicationInstance = ApplicationHandle; + + return parameters; + } +} diff --git a/src/InfiniFrame/Window/Builder/InfiniFrameWindowBuilderConfiguration.cs b/src/InfiniFrame.Window/Builder/InfiniFrameWindowBuilderConfiguration.cs similarity index 90% rename from src/InfiniFrame/Window/Builder/InfiniFrameWindowBuilderConfiguration.cs rename to src/InfiniFrame.Window/Builder/InfiniFrameWindowBuilderConfiguration.cs index e0c58d1b2..f73e500da 100644 --- a/src/InfiniFrame/Window/Builder/InfiniFrameWindowBuilderConfiguration.cs +++ b/src/InfiniFrame.Window/Builder/InfiniFrameWindowBuilderConfiguration.cs @@ -1,9 +1,9 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; -namespace InfiniFrame; +namespace InfiniFrame.Window.Builder; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -24,7 +24,7 @@ public class InfiniFrameWindowBuilderConfiguration : IInfiniFrameWindowBuilderCo // Methods // ----------------------------------------------------------------------------------------------------------------- /// - public void ApplyToNativeParameters(ref InfiniFrameNativeParameters parameters) { + public void ApplyToNativeParameters(ref InfiniFrameNativeWindowParameters parameters) { // NativeParent is populated under a parent-handle lease immediately before native construction. } } diff --git a/src/InfiniFrame/Window/Builder/InfiniFrameWindowBuilderFeatures.cs b/src/InfiniFrame.Window/Builder/InfiniFrameWindowBuilderFeatures.cs similarity index 85% rename from src/InfiniFrame/Window/Builder/InfiniFrameWindowBuilderFeatures.cs rename to src/InfiniFrame.Window/Builder/InfiniFrameWindowBuilderFeatures.cs index 31cc03dc2..fccddc42a 100644 --- a/src/InfiniFrame/Window/Builder/InfiniFrameWindowBuilderFeatures.cs +++ b/src/InfiniFrame.Window/Builder/InfiniFrameWindowBuilderFeatures.cs @@ -1,9 +1,19 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Features.Browser; +using InfiniFrame.Window.Features.Debugging; +using InfiniFrame.Window.Features.Decorations; +using InfiniFrame.Window.Features.InstanceArbitration; +using InfiniFrame.Window.Features.Menu; +using InfiniFrame.Window.Features.Notifications; +using InfiniFrame.Window.Features.PageNavigation; +using InfiniFrame.Window.Features.Position; +using InfiniFrame.Window.Features.Size; +using InfiniFrame.Window.Features.State; -namespace InfiniFrame; +namespace InfiniFrame.Window.Builder; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -41,7 +51,7 @@ public sealed class InfiniFrameWindowBuilderFeatures : IInfiniFrameWindowBuilder /// Applies all configured feature settings to the native parameters. /// /// The native parameters to populate. - public void ApplyToNativeParameters(ref InfiniFrameNativeParameters parameters) { + public void ApplyToNativeParameters(ref InfiniFrameNativeWindowParameters parameters) { Debugging.ApplyToNativeParameters(ref parameters); Browser.ApplyToNativeParameters(ref parameters); Decorations.ApplyToNativeParameters(ref parameters); diff --git a/src/InfiniFrame/Window/Events/InfiniFrameEvents.CustomScheme.cs b/src/InfiniFrame.Window/Events/InfiniFrameEvents.CustomScheme.cs similarity index 98% rename from src/InfiniFrame/Window/Events/InfiniFrameEvents.CustomScheme.cs rename to src/InfiniFrame.Window/Events/InfiniFrameEvents.CustomScheme.cs index 075828681..4de37650c 100644 --- a/src/InfiniFrame/Window/Events/InfiniFrameEvents.CustomScheme.cs +++ b/src/InfiniFrame.Window/Events/InfiniFrameEvents.CustomScheme.cs @@ -5,11 +5,11 @@ using System.Runtime.InteropServices; using System.Text; using InfiniFrame.NativeBridge.Delegates; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; using InfiniFrame.Utilities; using Microsoft.Extensions.Logging; -namespace InfiniFrame; +namespace InfiniFrame.Window.Events; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -67,7 +67,7 @@ public int OnCustomScheme(string url, ref CustomSchemeResponse response) { // ----------------------------------------------------------------------------------------------------------------- // Methods // ----------------------------------------------------------------------------------------------------------------- - private void ApplyCustomSchemeNames(ref InfiniFrameNativeParameters startupParameters) { + private void ApplyCustomSchemeNames(ref InfiniFrameNativeWindowParameters startupParameters) { var availableHandlers = new HashSet(EventsStore.CustomScheme.Snapshot.Select(static item => item.Key), StringComparer.Ordinal); var seen = new HashSet(StringComparer.Ordinal); diff --git a/src/InfiniFrame/Window/Events/InfiniFrameEvents.Debug.cs b/src/InfiniFrame.Window/Events/InfiniFrameEvents.Debug.cs similarity index 98% rename from src/InfiniFrame/Window/Events/InfiniFrameEvents.Debug.cs rename to src/InfiniFrame.Window/Events/InfiniFrameEvents.Debug.cs index cd7f3b575..14f21ff6e 100644 --- a/src/InfiniFrame/Window/Events/InfiniFrameEvents.Debug.cs +++ b/src/InfiniFrame.Window/Events/InfiniFrameEvents.Debug.cs @@ -3,7 +3,7 @@ // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame.Debugging; -namespace InfiniFrame; +namespace InfiniFrame.Window.Events; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Events/InfiniFrameEvents.DefaultEvents.cs b/src/InfiniFrame.Window/Events/InfiniFrameEvents.DefaultEvents.cs similarity index 96% rename from src/InfiniFrame/Window/Events/InfiniFrameEvents.DefaultEvents.cs rename to src/InfiniFrame.Window/Events/InfiniFrameEvents.DefaultEvents.cs index 5a148ad61..bbcf5389b 100644 --- a/src/InfiniFrame/Window/Events/InfiniFrameEvents.DefaultEvents.cs +++ b/src/InfiniFrame.Window/Events/InfiniFrameEvents.DefaultEvents.cs @@ -4,7 +4,7 @@ using System.Diagnostics; using Microsoft.Extensions.Logging; -namespace InfiniFrame; +namespace InfiniFrame.Window.Events; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -27,7 +27,6 @@ private void CloseChildWindows(IInfiniFrameWindow window) { if (config.ChildWindowsInternal.Count <= 0) return;// No child windows to close childWindows = [.. config.ChildWindowsInternal]; - config.ChildWindowsInternal.Clear(); } Logger.LogDebug("Lifecycle child windows"); diff --git a/src/InfiniFrame/Window/Events/InfiniFrameEvents.Messaging.cs b/src/InfiniFrame.Window/Events/InfiniFrameEvents.Messaging.cs similarity index 98% rename from src/InfiniFrame/Window/Events/InfiniFrameEvents.Messaging.cs rename to src/InfiniFrame.Window/Events/InfiniFrameEvents.Messaging.cs index e037bc580..3a70a133a 100644 --- a/src/InfiniFrame/Window/Events/InfiniFrameEvents.Messaging.cs +++ b/src/InfiniFrame.Window/Events/InfiniFrameEvents.Messaging.cs @@ -2,11 +2,11 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using System.Text.Json; -using InfiniFrame.Interop; +using InfiniFrame.Js.Interop; using InfiniFrame.Utilities; using Microsoft.Extensions.Logging; -namespace InfiniFrame; +namespace InfiniFrame.Window.Events; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Events/InfiniFrameEvents.cs b/src/InfiniFrame.Window/Events/InfiniFrameEvents.cs similarity index 98% rename from src/InfiniFrame/Window/Events/InfiniFrameEvents.cs rename to src/InfiniFrame.Window/Events/InfiniFrameEvents.cs index 106a13772..72bade073 100644 --- a/src/InfiniFrame/Window/Events/InfiniFrameEvents.cs +++ b/src/InfiniFrame.Window/Events/InfiniFrameEvents.cs @@ -6,11 +6,11 @@ using System.Runtime.InteropServices; using InfiniFrame.DragDrop; using InfiniFrame.NativeBridge.Delegates; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; using InfiniFrame.Utilities; using Microsoft.Extensions.Logging; -namespace InfiniFrame; +namespace InfiniFrame.Window.Events; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -102,7 +102,7 @@ public void PopulateFromBuilderEventStore(IInfiniFrameEventsStore eventStore) { /// Binds all native callback delegates to the native startup parameters for window creation. /// /// The native parameters to populate with callback delegates. - public void AssignToNativeParameters(ref InfiniFrameNativeParameters parameters) { + public void AssignToNativeParameters(ref InfiniFrameNativeWindowParameters parameters) { // Rebind callbacks to the per-window event instance that has Sender set via CompleteSetup. parameters.ClosedHandler = ClosedHandler; parameters.ClosingHandler = ClosingHandler; diff --git a/src/InfiniFrame/Window/Events/InfiniFrameEventsStore.cs b/src/InfiniFrame.Window/Events/InfiniFrameEventsStore.cs similarity index 99% rename from src/InfiniFrame/Window/Events/InfiniFrameEventsStore.cs rename to src/InfiniFrame.Window/Events/InfiniFrameEventsStore.cs index 67f392094..40e9035cf 100644 --- a/src/InfiniFrame/Window/Events/InfiniFrameEventsStore.cs +++ b/src/InfiniFrame.Window/Events/InfiniFrameEventsStore.cs @@ -5,7 +5,7 @@ using InfiniFrame.Debugging; using InfiniFrame.DragDrop; -namespace InfiniFrame; +namespace InfiniFrame.Window.Events; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/Browser/BrowserInfiniFrameWindowBuilderFeature.cs b/src/InfiniFrame.Window/Features/Browser/BrowserInfiniFrameWindowBuilderFeature.cs similarity index 92% rename from src/InfiniFrame/Window/Features/Browser/BrowserInfiniFrameWindowBuilderFeature.cs rename to src/InfiniFrame.Window/Features/Browser/BrowserInfiniFrameWindowBuilderFeature.cs index 48534e807..18e35f19d 100644 --- a/src/InfiniFrame/Window/Features/Browser/BrowserInfiniFrameWindowBuilderFeature.cs +++ b/src/InfiniFrame.Window/Features/Browser/BrowserInfiniFrameWindowBuilderFeature.cs @@ -1,9 +1,9 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.Browser; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -58,9 +58,6 @@ public class BrowserInfiniFrameWindowBuilderFeature : IBrowserInfiniFrameWindowB Environment.ProcessId.ToString() ); - /// - public string? WebView2RuntimePath { get; private set; } - // ----------------------------------------------------------------------------------------------------------------- // Methods // ----------------------------------------------------------------------------------------------------------------- @@ -152,17 +149,11 @@ public void SetTemporaryFilesPath(string path) { TemporaryFilesPath = Path.GetFullPath(path); } - /// - public void SetWebView2RuntimePath(string path) { - ArgumentException.ThrowIfNullOrWhiteSpace(path); - WebView2RuntimePath = Path.GetFullPath(path); - } - /// /// Applies all browser feature settings to the native parameters. /// /// The native parameters to populate. - public void ApplyToNativeParameters(ref InfiniFrameNativeParameters parameters) { + public void ApplyToNativeParameters(ref InfiniFrameNativeWindowParameters parameters) { parameters.ContextMenuEnabled = IsContextMenuEnabled; parameters.MediaAutoplayEnabled = IsMediaAutoplayEnabled; parameters.UserAgent = UserAgent; @@ -177,6 +168,5 @@ public void ApplyToNativeParameters(ref InfiniFrameNativeParameters parameters) parameters.BrowserShortcutsEnabled = IsBrowserShortcutsEnabled; parameters.BrowserControlInitParameters = BrowserControlInitParameters; parameters.TemporaryFilesPath = TemporaryFilesPath; - parameters.WebView2RuntimePath = WebView2RuntimePath; } } diff --git a/src/InfiniFrame/Window/Features/Browser/BrowserInfiniFrameWindowFeature.cs b/src/InfiniFrame.Window/Features/Browser/BrowserInfiniFrameWindowFeature.cs similarity index 99% rename from src/InfiniFrame/Window/Features/Browser/BrowserInfiniFrameWindowFeature.cs rename to src/InfiniFrame.Window/Features/Browser/BrowserInfiniFrameWindowFeature.cs index 4649a7556..1e12fc787 100644 --- a/src/InfiniFrame/Window/Features/Browser/BrowserInfiniFrameWindowFeature.cs +++ b/src/InfiniFrame.Window/Features/Browser/BrowserInfiniFrameWindowFeature.cs @@ -6,7 +6,7 @@ using InfiniFrame.NativeBridge; using Microsoft.Extensions.Logging; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.Browser; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/Browser/BrowserWebMessageDispatcher.cs b/src/InfiniFrame.Window/Features/Browser/BrowserWebMessageDispatcher.cs similarity index 96% rename from src/InfiniFrame/Window/Features/Browser/BrowserWebMessageDispatcher.cs rename to src/InfiniFrame.Window/Features/Browser/BrowserWebMessageDispatcher.cs index 0744a8f19..3c2d3ed17 100644 --- a/src/InfiniFrame/Window/Features/Browser/BrowserWebMessageDispatcher.cs +++ b/src/InfiniFrame.Window/Features/Browser/BrowserWebMessageDispatcher.cs @@ -2,8 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using System.Text.Json; +using InfiniFrame.Window.Features.WebMessaging.Handlers; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.Browser; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/Debugging/DebugEndpointResult.cs b/src/InfiniFrame.Window/Features/Debugging/DebugEndpointResult.cs similarity index 92% rename from src/InfiniFrame/Window/Features/Debugging/DebugEndpointResult.cs rename to src/InfiniFrame.Window/Features/Debugging/DebugEndpointResult.cs index 9e0542084..831173e00 100644 --- a/src/InfiniFrame/Window/Features/Debugging/DebugEndpointResult.cs +++ b/src/InfiniFrame.Window/Features/Debugging/DebugEndpointResult.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.Debugging; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/Debugging/DebuggingInfiniFrameWindowBuilderFeature.cs b/src/InfiniFrame.Window/Features/Debugging/DebuggingInfiniFrameWindowBuilderFeature.cs similarity index 94% rename from src/InfiniFrame/Window/Features/Debugging/DebuggingInfiniFrameWindowBuilderFeature.cs rename to src/InfiniFrame.Window/Features/Debugging/DebuggingInfiniFrameWindowBuilderFeature.cs index 44fd97157..a784adcfa 100644 --- a/src/InfiniFrame/Window/Features/Debugging/DebuggingInfiniFrameWindowBuilderFeature.cs +++ b/src/InfiniFrame.Window/Features/Debugging/DebuggingInfiniFrameWindowBuilderFeature.cs @@ -2,10 +2,10 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using System.Runtime.Versioning; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; using InfiniFrame.Utilities; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.Debugging; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -57,7 +57,7 @@ public IDebuggingInfiniFrameWindowBuilderFeature SetRemoteDebuggingPort(int port return this; } - public void ApplyToNativeParameters(ref InfiniFrameNativeParameters parameters) { + public void ApplyToNativeParameters(ref InfiniFrameNativeWindowParameters parameters) { parameters.DevToolsEnabled = IsDevToolsEnabled; parameters.WebInspectorEnabled = IsWebInspectorEnabled; parameters.RemoteDebuggingPort = RemoteDebuggingPort; diff --git a/src/InfiniFrame/Window/Features/Debugging/DebuggingInfiniFrameWindowFeature.cs b/src/InfiniFrame.Window/Features/Debugging/DebuggingInfiniFrameWindowFeature.cs similarity index 99% rename from src/InfiniFrame/Window/Features/Debugging/DebuggingInfiniFrameWindowFeature.cs rename to src/InfiniFrame.Window/Features/Debugging/DebuggingInfiniFrameWindowFeature.cs index 0dcc192ba..63f6c88f8 100644 --- a/src/InfiniFrame/Window/Features/Debugging/DebuggingInfiniFrameWindowFeature.cs +++ b/src/InfiniFrame.Window/Features/Debugging/DebuggingInfiniFrameWindowFeature.cs @@ -8,7 +8,7 @@ using InfiniFrame.Utilities; using Microsoft.Extensions.Logging; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.Debugging; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/Debugging/DebuggingWebMessageDispatcher.cs b/src/InfiniFrame.Window/Features/Debugging/DebuggingWebMessageDispatcher.cs similarity index 96% rename from src/InfiniFrame/Window/Features/Debugging/DebuggingWebMessageDispatcher.cs rename to src/InfiniFrame.Window/Features/Debugging/DebuggingWebMessageDispatcher.cs index 96af5d5d0..e6048c8f7 100644 --- a/src/InfiniFrame/Window/Features/Debugging/DebuggingWebMessageDispatcher.cs +++ b/src/InfiniFrame.Window/Features/Debugging/DebuggingWebMessageDispatcher.cs @@ -2,8 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using System.Text.Json; +using InfiniFrame.Window.Features.WebMessaging.Handlers; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.Debugging; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/Decorations/DecorationsInfiniFrameWindowBuilderFeature.cs b/src/InfiniFrame.Window/Features/Decorations/DecorationsInfiniFrameWindowBuilderFeature.cs similarity index 86% rename from src/InfiniFrame/Window/Features/Decorations/DecorationsInfiniFrameWindowBuilderFeature.cs rename to src/InfiniFrame.Window/Features/Decorations/DecorationsInfiniFrameWindowBuilderFeature.cs index 7d28b0602..a11e89d84 100644 --- a/src/InfiniFrame/Window/Features/Decorations/DecorationsInfiniFrameWindowBuilderFeature.cs +++ b/src/InfiniFrame.Window/Features/Decorations/DecorationsInfiniFrameWindowBuilderFeature.cs @@ -1,10 +1,10 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; using InfiniFrame.Utilities; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.Decorations; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -28,9 +28,6 @@ public class DecorationsInfiniFrameWindowBuilderFeature : IDecorationsInfiniFram /// public string? IconFilePath { get; private set; } - /// - public string? WindowsAppUserModelId { get; private set; } - /// public bool LimitLinuxWindowTitleLength { get; private set; } @@ -62,24 +59,18 @@ public void SetIconFile(string iconFilePath) { IconFilePath = iconFilePath; } - /// - public void SetWindowsAppUserModelId(string? appUserModelId) { - WindowsAppUserModelId = appUserModelId; - } - /// public void SetLimitLinuxWindowTitleLength(bool enabled) { LimitLinuxWindowTitleLength = enabled; } - public void ApplyToNativeParameters(ref InfiniFrameNativeParameters parameters) { + public void ApplyToNativeParameters(ref InfiniFrameNativeWindowParameters parameters) { parameters.Chromeless = IsChromeless; parameters.Transparent = IsTransparent; parameters.Title = Title; parameters.WindowIconFile = IconFileUtility.TryResolveIconFilePath(IconFilePath, out string? resolvedIconFilePath) ? resolvedIconFilePath : null; - parameters.WindowsAppUserModelId = WindowsAppUserModelId; ColorUtility.ParseBackgroundColor( BackgroundColor, out byte r, out byte g, out byte b, out byte a); diff --git a/src/InfiniFrame/Window/Features/Decorations/DecorationsInfiniFrameWindowFeature.cs b/src/InfiniFrame.Window/Features/Decorations/DecorationsInfiniFrameWindowFeature.cs similarity index 99% rename from src/InfiniFrame/Window/Features/Decorations/DecorationsInfiniFrameWindowFeature.cs rename to src/InfiniFrame.Window/Features/Decorations/DecorationsInfiniFrameWindowFeature.cs index 9192d73a7..060bd03ee 100644 --- a/src/InfiniFrame/Window/Features/Decorations/DecorationsInfiniFrameWindowFeature.cs +++ b/src/InfiniFrame.Window/Features/Decorations/DecorationsInfiniFrameWindowFeature.cs @@ -6,7 +6,7 @@ using InfiniFrame.Utilities; using Microsoft.Extensions.Logging; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.Decorations; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/Decorations/DecorationsWebMessageDispatcher.cs b/src/InfiniFrame.Window/Features/Decorations/DecorationsWebMessageDispatcher.cs similarity index 95% rename from src/InfiniFrame/Window/Features/Decorations/DecorationsWebMessageDispatcher.cs rename to src/InfiniFrame.Window/Features/Decorations/DecorationsWebMessageDispatcher.cs index 9e019bdf3..95ee7df62 100644 --- a/src/InfiniFrame/Window/Features/Decorations/DecorationsWebMessageDispatcher.cs +++ b/src/InfiniFrame.Window/Features/Decorations/DecorationsWebMessageDispatcher.cs @@ -2,8 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using System.Text.Json; +using InfiniFrame.Window.Features.WebMessaging.Handlers; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.Decorations; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/DragDrop/DragDropInfiniFrameWindowFeature.cs b/src/InfiniFrame.Window/Features/DragDrop/DragDropInfiniFrameWindowFeature.cs similarity index 97% rename from src/InfiniFrame/Window/Features/DragDrop/DragDropInfiniFrameWindowFeature.cs rename to src/InfiniFrame.Window/Features/DragDrop/DragDropInfiniFrameWindowFeature.cs index e2bc9295b..e35a8d9b1 100644 --- a/src/InfiniFrame/Window/Features/DragDrop/DragDropInfiniFrameWindowFeature.cs +++ b/src/InfiniFrame.Window/Features/DragDrop/DragDropInfiniFrameWindowFeature.cs @@ -5,7 +5,7 @@ using InfiniFrame.NativeBridge; using Microsoft.Extensions.Logging; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.DragDrop; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/FilePickerDialogs/FilePickerDialogsInfiniFrameWindowFeature.cs b/src/InfiniFrame.Window/Features/FilePickerDialogs/FilePickerDialogsInfiniFrameWindowFeature.cs similarity index 99% rename from src/InfiniFrame/Window/Features/FilePickerDialogs/FilePickerDialogsInfiniFrameWindowFeature.cs rename to src/InfiniFrame.Window/Features/FilePickerDialogs/FilePickerDialogsInfiniFrameWindowFeature.cs index 6ef296681..5d9aae2db 100644 --- a/src/InfiniFrame/Window/Features/FilePickerDialogs/FilePickerDialogsInfiniFrameWindowFeature.cs +++ b/src/InfiniFrame.Window/Features/FilePickerDialogs/FilePickerDialogsInfiniFrameWindowFeature.cs @@ -4,7 +4,7 @@ using InfiniFrame.NativeBridge; using Microsoft.Extensions.Logging; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.FilePickerDialogs; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/FilePickerDialogs/FilePickerDialogsWebMessageDispatcher.cs b/src/InfiniFrame.Window/Features/FilePickerDialogs/FilePickerDialogsWebMessageDispatcher.cs similarity index 95% rename from src/InfiniFrame/Window/Features/FilePickerDialogs/FilePickerDialogsWebMessageDispatcher.cs rename to src/InfiniFrame.Window/Features/FilePickerDialogs/FilePickerDialogsWebMessageDispatcher.cs index b3099aacf..b3be66ca7 100644 --- a/src/InfiniFrame/Window/Features/FilePickerDialogs/FilePickerDialogsWebMessageDispatcher.cs +++ b/src/InfiniFrame.Window/Features/FilePickerDialogs/FilePickerDialogsWebMessageDispatcher.cs @@ -2,8 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using System.Text.Json; +using InfiniFrame.Window.Features.WebMessaging.Handlers; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.FilePickerDialogs; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/FilePickerDialogs/InfiniFileDialogOperation.cs b/src/InfiniFrame.Window/Features/FilePickerDialogs/InfiniFileDialogOperation.cs similarity index 99% rename from src/InfiniFrame/Window/Features/FilePickerDialogs/InfiniFileDialogOperation.cs rename to src/InfiniFrame.Window/Features/FilePickerDialogs/InfiniFileDialogOperation.cs index 0c0422946..e4c8a0f18 100644 --- a/src/InfiniFrame/Window/Features/FilePickerDialogs/InfiniFileDialogOperation.cs +++ b/src/InfiniFrame.Window/Features/FilePickerDialogs/InfiniFileDialogOperation.cs @@ -8,7 +8,7 @@ using InfiniFrame.Utilities; using Microsoft.Extensions.Logging; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.FilePickerDialogs; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/FilePickerDialogs/WindowFeatureFilePickerFilter.cs b/src/InfiniFrame.Window/Features/FilePickerDialogs/WindowFeatureFilePickerFilter.cs similarity index 91% rename from src/InfiniFrame/Window/Features/FilePickerDialogs/WindowFeatureFilePickerFilter.cs rename to src/InfiniFrame.Window/Features/FilePickerDialogs/WindowFeatureFilePickerFilter.cs index 22a10b300..a26851ac2 100644 --- a/src/InfiniFrame/Window/Features/FilePickerDialogs/WindowFeatureFilePickerFilter.cs +++ b/src/InfiniFrame.Window/Features/FilePickerDialogs/WindowFeatureFilePickerFilter.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.FilePickerDialogs; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/InstanceArbitration/InstanceArbitration.cs b/src/InfiniFrame.Window/Features/InstanceArbitration/InstanceArbitration.cs similarity index 97% rename from src/InfiniFrame/Window/Features/InstanceArbitration/InstanceArbitration.cs rename to src/InfiniFrame.Window/Features/InstanceArbitration/InstanceArbitration.cs index 59d0b7370..7033ad997 100644 --- a/src/InfiniFrame/Window/Features/InstanceArbitration/InstanceArbitration.cs +++ b/src/InfiniFrame.Window/Features/InstanceArbitration/InstanceArbitration.cs @@ -4,9 +4,9 @@ using System.Runtime.Versioning; using System.Security; using System.Security.Principal; -using InfiniFrame.Utilities; +using InfiniFrame.NativeBridge; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.InstanceArbitration; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/InstanceArbitration/InstanceArbitrationInfiniFrameWindowBuilderFeature.cs b/src/InfiniFrame.Window/Features/InstanceArbitration/InstanceArbitrationInfiniFrameWindowBuilderFeature.cs similarity index 90% rename from src/InfiniFrame/Window/Features/InstanceArbitration/InstanceArbitrationInfiniFrameWindowBuilderFeature.cs rename to src/InfiniFrame.Window/Features/InstanceArbitration/InstanceArbitrationInfiniFrameWindowBuilderFeature.cs index ae6de2ecc..ab0d6fa66 100644 --- a/src/InfiniFrame/Window/Features/InstanceArbitration/InstanceArbitrationInfiniFrameWindowBuilderFeature.cs +++ b/src/InfiniFrame.Window/Features/InstanceArbitration/InstanceArbitrationInfiniFrameWindowBuilderFeature.cs @@ -1,9 +1,9 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.InstanceArbitration; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -35,7 +35,7 @@ public void SetMutexName(string mutexName) { /// Instance arbitration is a process-level concern (mutex + elevation detection) and does not /// map to any native window parameters. /// - public void ApplyToNativeParameters(ref InfiniFrameNativeParameters parameters) { + public void ApplyToNativeParameters(ref InfiniFrameNativeWindowParameters parameters) { // No-op: instance arbitration is process-level, not window-level. } } diff --git a/src/InfiniFrame/Window/Features/Invoke/InfiniDispatchOperation.cs b/src/InfiniFrame.Window/Features/Invoke/InfiniDispatchOperation.cs similarity index 99% rename from src/InfiniFrame/Window/Features/Invoke/InfiniDispatchOperation.cs rename to src/InfiniFrame.Window/Features/Invoke/InfiniDispatchOperation.cs index 33e6d6edb..279fb9d4c 100644 --- a/src/InfiniFrame/Window/Features/Invoke/InfiniDispatchOperation.cs +++ b/src/InfiniFrame.Window/Features/Invoke/InfiniDispatchOperation.cs @@ -8,7 +8,7 @@ using InfiniFrame.Utilities; using Microsoft.Extensions.Logging; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.Invoke; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/Invoke/InvokeInfiniFrameWindowFeature.cs b/src/InfiniFrame.Window/Features/Invoke/InvokeInfiniFrameWindowFeature.cs similarity index 98% rename from src/InfiniFrame/Window/Features/Invoke/InvokeInfiniFrameWindowFeature.cs rename to src/InfiniFrame.Window/Features/Invoke/InvokeInfiniFrameWindowFeature.cs index 9e6a2a499..309e0af17 100644 --- a/src/InfiniFrame/Window/Features/Invoke/InvokeInfiniFrameWindowFeature.cs +++ b/src/InfiniFrame.Window/Features/Invoke/InvokeInfiniFrameWindowFeature.cs @@ -5,7 +5,7 @@ using InfiniFrame.Utilities; using Microsoft.Extensions.Logging; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.Invoke; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/Invoke/InvokeWebMessageDispatcher.cs b/src/InfiniFrame.Window/Features/Invoke/InvokeWebMessageDispatcher.cs similarity index 91% rename from src/InfiniFrame/Window/Features/Invoke/InvokeWebMessageDispatcher.cs rename to src/InfiniFrame.Window/Features/Invoke/InvokeWebMessageDispatcher.cs index a7efe34c4..0e3217f34 100644 --- a/src/InfiniFrame/Window/Features/Invoke/InvokeWebMessageDispatcher.cs +++ b/src/InfiniFrame.Window/Features/Invoke/InvokeWebMessageDispatcher.cs @@ -1,7 +1,9 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -namespace InfiniFrame; +using InfiniFrame.Window.Features.WebMessaging.Handlers; + +namespace InfiniFrame.Window.Features.Invoke; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/JavaScript/JavaScriptInfiniFrameWindowFeature.cs b/src/InfiniFrame.Window/Features/JavaScript/JavaScriptInfiniFrameWindowFeature.cs similarity index 98% rename from src/InfiniFrame/Window/Features/JavaScript/JavaScriptInfiniFrameWindowFeature.cs rename to src/InfiniFrame.Window/Features/JavaScript/JavaScriptInfiniFrameWindowFeature.cs index fd129e94e..6393d9e0f 100644 --- a/src/InfiniFrame/Window/Features/JavaScript/JavaScriptInfiniFrameWindowFeature.cs +++ b/src/InfiniFrame.Window/Features/JavaScript/JavaScriptInfiniFrameWindowFeature.cs @@ -4,10 +4,11 @@ using System.Collections.Concurrent; using System.Text; using System.Text.Json; -using InfiniFrame.Interop; +using InfiniFrame.Js.Interop; +using InfiniFrame.Window.Features.WebMessaging; using Microsoft.Extensions.Logging; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.JavaScript; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/JavaScript/JavaScriptWebMessageDispatcher.cs b/src/InfiniFrame.Window/Features/JavaScript/JavaScriptWebMessageDispatcher.cs similarity index 94% rename from src/InfiniFrame/Window/Features/JavaScript/JavaScriptWebMessageDispatcher.cs rename to src/InfiniFrame.Window/Features/JavaScript/JavaScriptWebMessageDispatcher.cs index 34c19a7fa..a68ffb502 100644 --- a/src/InfiniFrame/Window/Features/JavaScript/JavaScriptWebMessageDispatcher.cs +++ b/src/InfiniFrame.Window/Features/JavaScript/JavaScriptWebMessageDispatcher.cs @@ -2,8 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using System.Text.Json; +using InfiniFrame.Window.Features.WebMessaging.Handlers; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.JavaScript; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/Lifecycle/LifecycleInfiniFrameWindowFeature.cs b/src/InfiniFrame.Window/Features/Lifecycle/LifecycleInfiniFrameWindowFeature.cs similarity index 92% rename from src/InfiniFrame/Window/Features/Lifecycle/LifecycleInfiniFrameWindowFeature.cs rename to src/InfiniFrame.Window/Features/Lifecycle/LifecycleInfiniFrameWindowFeature.cs index 4fddd60a3..c88cd23fb 100644 --- a/src/InfiniFrame/Window/Features/Lifecycle/LifecycleInfiniFrameWindowFeature.cs +++ b/src/InfiniFrame.Window/Features/Lifecycle/LifecycleInfiniFrameWindowFeature.cs @@ -6,11 +6,11 @@ using FluentValidation; using InfiniFrame.NativeBridge; using InfiniFrame.NativeBridge.Handles; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; using InfiniFrame.Utilities; using Microsoft.Extensions.Logging; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.Lifecycle; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -21,9 +21,10 @@ namespace InfiniFrame; public class LifecycleInfiniFrameWindowFeature( IInfiniFrameWindow window, ILogger logger, - IValidator validator + IValidator validator ) : ILifecycleInfiniFrameWindowFeature, IDisposable { private static readonly InfiniFrameNative.ContextAction ReadyCallback = OnNativeReady; + private static readonly InfiniFrameNative.ContextAction ReadyFailureCallback = OnNativeReadyFailure; private static readonly InfiniFrameNative.ContextAction TeardownCallback = OnNativeTeardown; private readonly TaskCompletionSource _closed = new(TaskCreationOptions.RunContinuationsAsynchronously); private readonly TaskCompletionSource _closedCallbacksDelivered = new(TaskCreationOptions.RunContinuationsAsynchronously); @@ -79,7 +80,7 @@ bool ILifecycleInfiniFrameWindowFeature.CanWaitForTeardownDuringDispose() /// void ILifecycleInfiniFrameWindowFeature.Initialize() { window.BeginInitialization(); - InfiniFrameNativeParameters startupParameters = window.Configuration.StartupParameters; + InfiniFrameNativeWindowParameters startupParameters = window.Configuration.StartupParameters; bool webInspectorEnabled = startupParameters.WebInspectorEnabled; try { @@ -258,8 +259,11 @@ public ValueTask WaitForCloseAsync(CancellationToken ct = default) => public ValueTask WaitForClosedCallbacksAsync(CancellationToken ct = default) => new(_closedCallbacksDelivered.Task.WaitAsync(ct)); - public ValueTask WaitForTeardownAsync(CancellationToken ct = default) - => new(_teardown.Task.WaitAsync(ct)); + public ValueTask WaitForTeardownAsync(CancellationToken ct = default) { + if (window.LifecycleState >= InfiniFrameWindowLifecycleState.TeardownComplete) + return ValueTask.CompletedTask; + return new(_teardown.Task.WaitAsync(ct)); + } /// public void Close() { @@ -335,6 +339,11 @@ void ILifecycleInfiniFrameWindowFeature.MarkAsClosed() { void ILifecycleInfiniFrameWindowFeature.MarkClosedCallbacksDelivered() => _closedCallbacksDelivered.TrySetResult(); + void ILifecycleInfiniFrameWindowFeature.CompleteTeardownAfterNativeLoop() { + if (window.LifecycleState >= InfiniFrameWindowLifecycleState.NativeClosed) + CompleteTeardown(); + } + void ILifecycleInfiniFrameWindowFeature.MarkCloseRejected() { TaskCompletionSource? attempt; lock (_closeAttemptLock) { @@ -362,9 +371,12 @@ private void Dispose(bool disposing) { if (window.LifecycleState < InfiniFrameWindowLifecycleState.TeardownComplete && window.LifecycleState != InfiniFrameWindowLifecycleState.Disposed) { - // The normal teardown path hasn't completed yet. Still release callback roots - // and milestones to avoid leaks, and release the native handle so .NET 10's - // runtime doesn't abort during shutdown over unreleased SafeHandles. + // Native teardown callbacks can still be executing. Keep the callback and + // milestone roots alive; CompleteTeardown performs the final release after + // WM_NCDESTROY has returned to the native message loop. + if (Volatile.Read(ref _closeRequestDispatched) != 0 || window.LifecycleState >= InfiniFrameWindowLifecycleState.NativeClosed) + return; + ReleaseNativeCallbackRootOnce(); ReleaseMilestoneRootOnce(); try { window.ReleaseNativeHandle(); } @@ -426,8 +438,10 @@ private void RegisterNativeMilestoneCallbacks(IntPtr handle) { _milestoneRoot = GCHandle.Alloc(this); IntPtr context = GCHandle.ToIntPtr(_milestoneRoot); InfiniFrameNativeInteropStatus readyStatus = InfiniFrameNative.SetReadyCallback(handle, ReadyCallback, context); + InfiniFrameNativeInteropStatus readyFailureStatus = InfiniFrameNative.SetReadyFailureCallback(handle, ReadyFailureCallback, context); InfiniFrameNativeInteropStatus teardownStatus = InfiniFrameNative.SetTeardownCallback(handle, TeardownCallback, context); if (readyStatus == InfiniFrameNativeInteropStatus.Success + && readyFailureStatus == InfiniFrameNativeInteropStatus.Success && teardownStatus == InfiniFrameNativeInteropStatus.Success) return; @@ -440,6 +454,12 @@ private static void OnNativeReady(IntPtr context) { lifecycle.CompleteReady(); } + private static void OnNativeReadyFailure(IntPtr context) { + if (!TryGetLifecycle(context, out LifecycleInfiniFrameWindowFeature? lifecycle)) return; + + lifecycle._ready.TrySetException(new InfiniFrameNativeInteropException("WebView2 initialization failed.")); + } + private static void OnNativeTeardown(IntPtr context) { if (!TryGetLifecycle(context, out LifecycleInfiniFrameWindowFeature? lifecycle)) return; diff --git a/src/InfiniFrame/Window/Features/Lifecycle/LifecycleWebMessageDispatcher.cs b/src/InfiniFrame.Window/Features/Lifecycle/LifecycleWebMessageDispatcher.cs similarity index 93% rename from src/InfiniFrame/Window/Features/Lifecycle/LifecycleWebMessageDispatcher.cs rename to src/InfiniFrame.Window/Features/Lifecycle/LifecycleWebMessageDispatcher.cs index 5eb8cf931..0ef701b20 100644 --- a/src/InfiniFrame/Window/Features/Lifecycle/LifecycleWebMessageDispatcher.cs +++ b/src/InfiniFrame.Window/Features/Lifecycle/LifecycleWebMessageDispatcher.cs @@ -2,8 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using System.Text.Json; +using InfiniFrame.Window.Features.WebMessaging.Handlers; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.Lifecycle; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/Menu/MenuInfiniFrameWindowBuilderFeature.cs b/src/InfiniFrame.Window/Features/Menu/MenuInfiniFrameWindowBuilderFeature.cs similarity index 89% rename from src/InfiniFrame/Window/Features/Menu/MenuInfiniFrameWindowBuilderFeature.cs rename to src/InfiniFrame.Window/Features/Menu/MenuInfiniFrameWindowBuilderFeature.cs index 438602662..a6643363b 100644 --- a/src/InfiniFrame/Window/Features/Menu/MenuInfiniFrameWindowBuilderFeature.cs +++ b/src/InfiniFrame.Window/Features/Menu/MenuInfiniFrameWindowBuilderFeature.cs @@ -2,9 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using System.Text.Json; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.Menu; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -25,7 +25,7 @@ public void SetMenuBar(InfiniFrameMenuBar? menuBar) { } /// - public void ApplyToNativeParameters(ref InfiniFrameNativeParameters parameters) { + public void ApplyToNativeParameters(ref InfiniFrameNativeWindowParameters parameters) { parameters.MenuBarJson = MenuBar.Items.IsEmpty ? null : JsonSerializer.Serialize(MenuBar, MenuJsonContext.Default.InfiniFrameMenuBar); diff --git a/src/InfiniFrame/Window/Features/Menu/MenuInfiniFrameWindowFeature.cs b/src/InfiniFrame.Window/Features/Menu/MenuInfiniFrameWindowFeature.cs similarity index 99% rename from src/InfiniFrame/Window/Features/Menu/MenuInfiniFrameWindowFeature.cs rename to src/InfiniFrame.Window/Features/Menu/MenuInfiniFrameWindowFeature.cs index 16c068a3a..80c3048ab 100644 --- a/src/InfiniFrame/Window/Features/Menu/MenuInfiniFrameWindowFeature.cs +++ b/src/InfiniFrame.Window/Features/Menu/MenuInfiniFrameWindowFeature.cs @@ -8,7 +8,7 @@ using InfiniFrame.Utilities; using Microsoft.Extensions.Logging; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.Menu; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/Menu/MenuJsonContext.cs b/src/InfiniFrame.Window/Features/Menu/MenuJsonContext.cs similarity index 95% rename from src/InfiniFrame/Window/Features/Menu/MenuJsonContext.cs rename to src/InfiniFrame.Window/Features/Menu/MenuJsonContext.cs index c9eb284a3..4fb4fc06c 100644 --- a/src/InfiniFrame/Window/Features/Menu/MenuJsonContext.cs +++ b/src/InfiniFrame.Window/Features/Menu/MenuJsonContext.cs @@ -4,7 +4,7 @@ using System.Text.Json; using System.Text.Json.Serialization; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.Menu; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/Monitors/MonitorsInfiniFrameWindowFeature.cs b/src/InfiniFrame.Window/Features/Monitors/MonitorsInfiniFrameWindowFeature.cs similarity index 97% rename from src/InfiniFrame/Window/Features/Monitors/MonitorsInfiniFrameWindowFeature.cs rename to src/InfiniFrame.Window/Features/Monitors/MonitorsInfiniFrameWindowFeature.cs index 9563e0723..c29f7c167 100644 --- a/src/InfiniFrame/Window/Features/Monitors/MonitorsInfiniFrameWindowFeature.cs +++ b/src/InfiniFrame.Window/Features/Monitors/MonitorsInfiniFrameWindowFeature.cs @@ -5,7 +5,7 @@ using InfiniFrame.Utilities; using Microsoft.Extensions.Logging; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.Monitors; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/Monitors/MonitorsWebMessageDispatcher.cs b/src/InfiniFrame.Window/Features/Monitors/MonitorsWebMessageDispatcher.cs similarity index 93% rename from src/InfiniFrame/Window/Features/Monitors/MonitorsWebMessageDispatcher.cs rename to src/InfiniFrame.Window/Features/Monitors/MonitorsWebMessageDispatcher.cs index 27b9adfcf..f159b828f 100644 --- a/src/InfiniFrame/Window/Features/Monitors/MonitorsWebMessageDispatcher.cs +++ b/src/InfiniFrame.Window/Features/Monitors/MonitorsWebMessageDispatcher.cs @@ -2,8 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using System.Text.Json; +using InfiniFrame.Window.Features.WebMessaging.Handlers; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.Monitors; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/Notifications/InfiniMessageDialogOperation.cs b/src/InfiniFrame.Window/Features/Notifications/InfiniMessageDialogOperation.cs similarity index 99% rename from src/InfiniFrame/Window/Features/Notifications/InfiniMessageDialogOperation.cs rename to src/InfiniFrame.Window/Features/Notifications/InfiniMessageDialogOperation.cs index 19e5ec151..64c93824b 100644 --- a/src/InfiniFrame/Window/Features/Notifications/InfiniMessageDialogOperation.cs +++ b/src/InfiniFrame.Window/Features/Notifications/InfiniMessageDialogOperation.cs @@ -9,7 +9,7 @@ using InfiniFrame.Utilities; using Microsoft.Extensions.Logging; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.Notifications; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/Notifications/InfiniNotificationOperation.cs b/src/InfiniFrame.Window/Features/Notifications/InfiniNotificationOperation.cs similarity index 99% rename from src/InfiniFrame/Window/Features/Notifications/InfiniNotificationOperation.cs rename to src/InfiniFrame.Window/Features/Notifications/InfiniNotificationOperation.cs index 8fa9200e0..ea3759600 100644 --- a/src/InfiniFrame/Window/Features/Notifications/InfiniNotificationOperation.cs +++ b/src/InfiniFrame.Window/Features/Notifications/InfiniNotificationOperation.cs @@ -8,7 +8,7 @@ using InfiniFrame.Utilities; using Microsoft.Extensions.Logging; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.Notifications; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/Notifications/NotificationsInfiniFrameWindowBuilderFeature.cs b/src/InfiniFrame.Window/Features/Notifications/NotificationsInfiniFrameWindowBuilderFeature.cs similarity index 90% rename from src/InfiniFrame/Window/Features/Notifications/NotificationsInfiniFrameWindowBuilderFeature.cs rename to src/InfiniFrame.Window/Features/Notifications/NotificationsInfiniFrameWindowBuilderFeature.cs index aa79ca47c..d5a92330d 100644 --- a/src/InfiniFrame/Window/Features/Notifications/NotificationsInfiniFrameWindowBuilderFeature.cs +++ b/src/InfiniFrame.Window/Features/Notifications/NotificationsInfiniFrameWindowBuilderFeature.cs @@ -1,9 +1,9 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.Notifications; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -31,7 +31,7 @@ public void SetDefaultNotificationIcon(string? iconPath) { DefaultNotificationIcon = iconPath; } - public void ApplyToNativeParameters(ref InfiniFrameNativeParameters parameters) { + public void ApplyToNativeParameters(ref InfiniFrameNativeWindowParameters parameters) { parameters.NotificationsEnabled = IsNotificationsEnabled; parameters.DefaultNotificationIcon = DefaultNotificationIcon; } diff --git a/src/InfiniFrame/Window/Features/Notifications/NotificationsInfiniFrameWindowFeature.cs b/src/InfiniFrame.Window/Features/Notifications/NotificationsInfiniFrameWindowFeature.cs similarity index 94% rename from src/InfiniFrame/Window/Features/Notifications/NotificationsInfiniFrameWindowFeature.cs rename to src/InfiniFrame.Window/Features/Notifications/NotificationsInfiniFrameWindowFeature.cs index c305ef4c8..5c4661e46 100644 --- a/src/InfiniFrame/Window/Features/Notifications/NotificationsInfiniFrameWindowFeature.cs +++ b/src/InfiniFrame.Window/Features/Notifications/NotificationsInfiniFrameWindowFeature.cs @@ -7,7 +7,7 @@ using InfiniFrame.NativeBridge.Dialogs; using Microsoft.Extensions.Logging; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.Notifications; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -20,11 +20,6 @@ public class NotificationsInfiniFrameWindowFeature( ILogger logger ) : INotificationsInfiniFrameWindowFeature { - /// - /// Gets the notification registration identifier from the window's startup parameters. - /// - public string? NotificationRegistrationId => window.Configuration.StartupParameters.NotificationRegistrationId; - /// /// Gets whether desktop notifications are enabled for this window. /// diff --git a/src/InfiniFrame/Window/Features/Notifications/NotificationsWebMessageDispatcher.cs b/src/InfiniFrame.Window/Features/Notifications/NotificationsWebMessageDispatcher.cs similarity index 96% rename from src/InfiniFrame/Window/Features/Notifications/NotificationsWebMessageDispatcher.cs rename to src/InfiniFrame.Window/Features/Notifications/NotificationsWebMessageDispatcher.cs index b64433af0..660b9595e 100644 --- a/src/InfiniFrame/Window/Features/Notifications/NotificationsWebMessageDispatcher.cs +++ b/src/InfiniFrame.Window/Features/Notifications/NotificationsWebMessageDispatcher.cs @@ -3,8 +3,9 @@ // --------------------------------------------------------------------------------------------------------------------- using System.Text.Json; using InfiniFrame.NativeBridge.Dialogs; +using InfiniFrame.Window.Features.WebMessaging.Handlers; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.Notifications; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/PageNavigation/InfiniNavigationOperation.cs b/src/InfiniFrame.Window/Features/PageNavigation/InfiniNavigationOperation.cs similarity index 99% rename from src/InfiniFrame/Window/Features/PageNavigation/InfiniNavigationOperation.cs rename to src/InfiniFrame.Window/Features/PageNavigation/InfiniNavigationOperation.cs index 4eb462261..1b51d7a01 100644 --- a/src/InfiniFrame/Window/Features/PageNavigation/InfiniNavigationOperation.cs +++ b/src/InfiniFrame.Window/Features/PageNavigation/InfiniNavigationOperation.cs @@ -7,7 +7,7 @@ using InfiniFrame.NativeBridge.Handles; using Microsoft.Extensions.Logging; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.PageNavigation; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/PageNavigation/PageNavigationInfiniFrameWindowBuilderFeature.cs b/src/InfiniFrame.Window/Features/PageNavigation/PageNavigationInfiniFrameWindowBuilderFeature.cs similarity index 90% rename from src/InfiniFrame/Window/Features/PageNavigation/PageNavigationInfiniFrameWindowBuilderFeature.cs rename to src/InfiniFrame.Window/Features/PageNavigation/PageNavigationInfiniFrameWindowBuilderFeature.cs index 1a593b564..053cfed86 100644 --- a/src/InfiniFrame/Window/Features/PageNavigation/PageNavigationInfiniFrameWindowBuilderFeature.cs +++ b/src/InfiniFrame.Window/Features/PageNavigation/PageNavigationInfiniFrameWindowBuilderFeature.cs @@ -1,9 +1,9 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.PageNavigation; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -34,7 +34,7 @@ public void SetUrl(Uri? startUrl) { StartUrl = startUrl?.ToString(); } - public void ApplyToNativeParameters(ref InfiniFrameNativeParameters parameters) { + public void ApplyToNativeParameters(ref InfiniFrameNativeWindowParameters parameters) { parameters.StartUrl = StartUrl; parameters.StartString = StartString; } diff --git a/src/InfiniFrame/Window/Features/PageNavigation/PageNavigationInfiniFrameWindowFeature.cs b/src/InfiniFrame.Window/Features/PageNavigation/PageNavigationInfiniFrameWindowFeature.cs similarity index 99% rename from src/InfiniFrame/Window/Features/PageNavigation/PageNavigationInfiniFrameWindowFeature.cs rename to src/InfiniFrame.Window/Features/PageNavigation/PageNavigationInfiniFrameWindowFeature.cs index 02259b056..9f1126dea 100644 --- a/src/InfiniFrame/Window/Features/PageNavigation/PageNavigationInfiniFrameWindowFeature.cs +++ b/src/InfiniFrame.Window/Features/PageNavigation/PageNavigationInfiniFrameWindowFeature.cs @@ -8,7 +8,7 @@ using InfiniFrame.Utilities; using Microsoft.Extensions.Logging; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.PageNavigation; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/PageNavigation/PageNavigationWebMessageDispatcher.cs b/src/InfiniFrame.Window/Features/PageNavigation/PageNavigationWebMessageDispatcher.cs similarity index 95% rename from src/InfiniFrame/Window/Features/PageNavigation/PageNavigationWebMessageDispatcher.cs rename to src/InfiniFrame.Window/Features/PageNavigation/PageNavigationWebMessageDispatcher.cs index e046cdb6e..8dd02b8de 100644 --- a/src/InfiniFrame/Window/Features/PageNavigation/PageNavigationWebMessageDispatcher.cs +++ b/src/InfiniFrame.Window/Features/PageNavigation/PageNavigationWebMessageDispatcher.cs @@ -2,8 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using System.Text.Json; +using InfiniFrame.Window.Features.WebMessaging.Handlers; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.PageNavigation; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/Position/PositionInfiniFrameWindowBuilderFeature.cs b/src/InfiniFrame.Window/Features/Position/PositionInfiniFrameWindowBuilderFeature.cs similarity index 94% rename from src/InfiniFrame/Window/Features/Position/PositionInfiniFrameWindowBuilderFeature.cs rename to src/InfiniFrame.Window/Features/Position/PositionInfiniFrameWindowBuilderFeature.cs index d4203a3c7..6b273e5e0 100644 --- a/src/InfiniFrame/Window/Features/Position/PositionInfiniFrameWindowBuilderFeature.cs +++ b/src/InfiniFrame.Window/Features/Position/PositionInfiniFrameWindowBuilderFeature.cs @@ -2,9 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using System.Drawing; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.Position; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -57,7 +57,7 @@ public void CenteredOnMainMonitor(bool enabled) { StartCentered = enabled; } - public void ApplyToNativeParameters(ref InfiniFrameNativeParameters parameters) { + public void ApplyToNativeParameters(ref InfiniFrameNativeWindowParameters parameters) { parameters.Top = Top; parameters.Left = Left; parameters.CenterOnInitialize = StartCentered; diff --git a/src/InfiniFrame/Window/Features/Position/PositionInfiniFrameWindowFeature.cs b/src/InfiniFrame.Window/Features/Position/PositionInfiniFrameWindowFeature.cs similarity index 99% rename from src/InfiniFrame/Window/Features/Position/PositionInfiniFrameWindowFeature.cs rename to src/InfiniFrame.Window/Features/Position/PositionInfiniFrameWindowFeature.cs index 7bccce9f6..a396cbb99 100644 --- a/src/InfiniFrame/Window/Features/Position/PositionInfiniFrameWindowFeature.cs +++ b/src/InfiniFrame.Window/Features/Position/PositionInfiniFrameWindowFeature.cs @@ -8,7 +8,7 @@ using InfiniFrame.Utilities; using Microsoft.Extensions.Logging; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.Position; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/Position/PositionWebMessageDispatcher.cs b/src/InfiniFrame.Window/Features/Position/PositionWebMessageDispatcher.cs similarity index 96% rename from src/InfiniFrame/Window/Features/Position/PositionWebMessageDispatcher.cs rename to src/InfiniFrame.Window/Features/Position/PositionWebMessageDispatcher.cs index 111953d54..6e6b307f4 100644 --- a/src/InfiniFrame/Window/Features/Position/PositionWebMessageDispatcher.cs +++ b/src/InfiniFrame.Window/Features/Position/PositionWebMessageDispatcher.cs @@ -2,8 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using System.Text.Json; +using InfiniFrame.Window.Features.WebMessaging.Handlers; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.Position; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/Size/SizeInfiniFrameWindowBuilderFeature.cs b/src/InfiniFrame.Window/Features/Size/SizeInfiniFrameWindowBuilderFeature.cs similarity index 93% rename from src/InfiniFrame/Window/Features/Size/SizeInfiniFrameWindowBuilderFeature.cs rename to src/InfiniFrame.Window/Features/Size/SizeInfiniFrameWindowBuilderFeature.cs index 6e1461b56..ec261b0ae 100644 --- a/src/InfiniFrame/Window/Features/Size/SizeInfiniFrameWindowBuilderFeature.cs +++ b/src/InfiniFrame.Window/Features/Size/SizeInfiniFrameWindowBuilderFeature.cs @@ -2,9 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using System.Drawing; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.Size; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -40,7 +40,7 @@ public void SetSize(int width, int height) { Height = height; } /// - public void SetSize(Size size) { + public void SetSize(System.Drawing.Size size) { StartWithOsDefaultSize = false; Width = size.Width; Height = size.Height; @@ -61,7 +61,7 @@ public void SetMaxSize(int maxWidth, int maxHeight) { MaxHeight = maxHeight; } /// - public void SetMaxSize(Size size) { + public void SetMaxSize(System.Drawing.Size size) { MaxWidth = size.Width; MaxHeight = size.Height; } @@ -79,7 +79,7 @@ public void SetMinSize(int minWidth, int minHeight) { MinHeight = minHeight; } /// - public void SetMinSize(Size size) { + public void SetMinSize(System.Drawing.Size size) { MinWidth = size.Width; MinHeight = size.Height; } @@ -100,7 +100,7 @@ public void SetResizable(bool enabled = true) { IsResizable = enabled; } - public void ApplyToNativeParameters(ref InfiniFrameNativeParameters parameters) { + public void ApplyToNativeParameters(ref InfiniFrameNativeWindowParameters parameters) { parameters.Height = Height; parameters.Width = Width; parameters.MaxHeight = MaxHeight; diff --git a/src/InfiniFrame/Window/Features/Size/SizeInfiniFrameWindowFeature.cs b/src/InfiniFrame.Window/Features/Size/SizeInfiniFrameWindowFeature.cs similarity index 92% rename from src/InfiniFrame/Window/Features/Size/SizeInfiniFrameWindowFeature.cs rename to src/InfiniFrame.Window/Features/Size/SizeInfiniFrameWindowFeature.cs index acc0b0a9f..9502c451c 100644 --- a/src/InfiniFrame/Window/Features/Size/SizeInfiniFrameWindowFeature.cs +++ b/src/InfiniFrame.Window/Features/Size/SizeInfiniFrameWindowFeature.cs @@ -2,12 +2,11 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using System.Diagnostics; -using System.Drawing; using InfiniFrame.NativeBridge; using InfiniFrame.Utilities; using Microsoft.Extensions.Logging; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.Size; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -22,13 +21,13 @@ ILogger logger /// [DebuggerBrowsable(DebuggerBrowsableState.Never)] - public Size Size => NativeInvoke.InvokeSyncWithValidation( + public System.Drawing.Size Size => NativeInvoke.InvokeSyncWithValidation( logger, window, window.ManagedThreadId, - callback: (IntPtr handle, out Size value) => { + callback: (IntPtr handle, out System.Drawing.Size value) => { InfiniFrameNativeInteropStatus status = InfiniFrameNative.GetSize(handle, out int width, out int height); - value = new Size(width, height); + value = new System.Drawing.Size(width, height); return status; } ); @@ -54,13 +53,13 @@ ILogger logger /// [DebuggerBrowsable(DebuggerBrowsableState.Never)] - public Size MaxSize => NativeInvoke.InvokeSyncWithValidation( + public System.Drawing.Size MaxSize => NativeInvoke.InvokeSyncWithValidation( logger, window, window.ManagedThreadId, - callback: (IntPtr handle, out Size value) => { + callback: (IntPtr handle, out System.Drawing.Size value) => { InfiniFrameNativeInteropStatus status = InfiniFrameNative.GetMaxSize(handle, out int width, out int height); - value = new Size(width, height); + value = new System.Drawing.Size(width, height); return status; } ); @@ -85,13 +84,13 @@ ILogger logger /// [DebuggerBrowsable(DebuggerBrowsableState.Never)] - public Size MinSize => NativeInvoke.InvokeSyncWithValidation( + public System.Drawing.Size MinSize => NativeInvoke.InvokeSyncWithValidation( logger, window, window.ManagedThreadId, - callback: (IntPtr handle, out Size value) => { + callback: (IntPtr handle, out System.Drawing.Size value) => { InfiniFrameNativeInteropStatus status = InfiniFrameNative.GetMinSize(handle, out int width, out int height); - value = new Size(width, height); + value = new System.Drawing.Size(width, height); return status; } ); @@ -143,7 +142,7 @@ public void SetSize(int width, int height) { // ReSharper disable once InvalidXmlDocComment /// - public void SetSize(Size size) + public void SetSize(System.Drawing.Size size) => SetSize(size.Width, size.Height); /// @@ -185,7 +184,7 @@ public void SetMaxSize(int maxWidth, int maxHeight) { /// Sets the maximum window size from a Size value. /// /// The maximum size to set. - public void SetMaxSize(Size size) + public void SetMaxSize(System.Drawing.Size size) => SetMaxSize(size.Width, size.Height); /// @@ -219,7 +218,7 @@ public void SetMinSize(int minWidth, int minHeight) { /// Sets the minimum window size from a Size value. /// /// The minimum size to set. - public void SetMinSize(Size size) + public void SetMinSize(System.Drawing.Size size) => SetMinSize(size.Width, size.Height); /// diff --git a/src/InfiniFrame/Window/Features/Size/SizeWebMessageDispatcher.cs b/src/InfiniFrame.Window/Features/Size/SizeWebMessageDispatcher.cs similarity index 97% rename from src/InfiniFrame/Window/Features/Size/SizeWebMessageDispatcher.cs rename to src/InfiniFrame.Window/Features/Size/SizeWebMessageDispatcher.cs index e9565a3da..de498043e 100644 --- a/src/InfiniFrame/Window/Features/Size/SizeWebMessageDispatcher.cs +++ b/src/InfiniFrame.Window/Features/Size/SizeWebMessageDispatcher.cs @@ -2,8 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using System.Text.Json; +using InfiniFrame.Window.Features.WebMessaging.Handlers; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.Size; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/State/StateInfiniFrameWindowBuilderFeature.cs b/src/InfiniFrame.Window/Features/State/StateInfiniFrameWindowBuilderFeature.cs similarity index 94% rename from src/InfiniFrame/Window/Features/State/StateInfiniFrameWindowBuilderFeature.cs rename to src/InfiniFrame.Window/Features/State/StateInfiniFrameWindowBuilderFeature.cs index 5b9d80134..558b7a587 100644 --- a/src/InfiniFrame/Window/Features/State/StateInfiniFrameWindowBuilderFeature.cs +++ b/src/InfiniFrame.Window/Features/State/StateInfiniFrameWindowBuilderFeature.cs @@ -1,9 +1,9 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.State; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -59,7 +59,7 @@ public void EnableZoom(bool zoomEnabled) { IsZoomEnabled = zoomEnabled; } - public void ApplyToNativeParameters(ref InfiniFrameNativeParameters parameters) { + public void ApplyToNativeParameters(ref InfiniFrameNativeWindowParameters parameters) { parameters.FullScreen = StartFullScreen; parameters.Maximized = StartMaximized; parameters.Minimized = StartMinimized; diff --git a/src/InfiniFrame/Window/Features/State/StateInfiniFrameWindowFeature.cs b/src/InfiniFrame.Window/Features/State/StateInfiniFrameWindowFeature.cs similarity index 99% rename from src/InfiniFrame/Window/Features/State/StateInfiniFrameWindowFeature.cs rename to src/InfiniFrame.Window/Features/State/StateInfiniFrameWindowFeature.cs index ab9f80830..417b94e28 100644 --- a/src/InfiniFrame/Window/Features/State/StateInfiniFrameWindowFeature.cs +++ b/src/InfiniFrame.Window/Features/State/StateInfiniFrameWindowFeature.cs @@ -8,7 +8,7 @@ using InfiniFrame.Utilities; using Microsoft.Extensions.Logging; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.State; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/State/StateWebMessageDispatcher.cs b/src/InfiniFrame.Window/Features/State/StateWebMessageDispatcher.cs similarity index 96% rename from src/InfiniFrame/Window/Features/State/StateWebMessageDispatcher.cs rename to src/InfiniFrame.Window/Features/State/StateWebMessageDispatcher.cs index 8737b046c..f8bf7119f 100644 --- a/src/InfiniFrame/Window/Features/State/StateWebMessageDispatcher.cs +++ b/src/InfiniFrame.Window/Features/State/StateWebMessageDispatcher.cs @@ -3,8 +3,9 @@ // --------------------------------------------------------------------------------------------------------------------- using System.Drawing; using System.Text.Json; +using InfiniFrame.Window.Features.WebMessaging.Handlers; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.State; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/Taskbar/TaskbarInfiniFrameWindowFeature.cs b/src/InfiniFrame.Window/Features/Taskbar/TaskbarInfiniFrameWindowFeature.cs similarity index 98% rename from src/InfiniFrame/Window/Features/Taskbar/TaskbarInfiniFrameWindowFeature.cs rename to src/InfiniFrame.Window/Features/Taskbar/TaskbarInfiniFrameWindowFeature.cs index 9e9d6f25f..f7aaf0a96 100644 --- a/src/InfiniFrame/Window/Features/Taskbar/TaskbarInfiniFrameWindowFeature.cs +++ b/src/InfiniFrame.Window/Features/Taskbar/TaskbarInfiniFrameWindowFeature.cs @@ -5,7 +5,7 @@ using InfiniFrame.NativeBridge; using Microsoft.Extensions.Logging; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.Taskbar; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/WebMessaging/CamelCaseEnumWebMessageJsonConverter.cs b/src/InfiniFrame.Window/Features/WebMessaging/CamelCaseEnumWebMessageJsonConverter.cs similarity index 93% rename from src/InfiniFrame/Window/Features/WebMessaging/CamelCaseEnumWebMessageJsonConverter.cs rename to src/InfiniFrame.Window/Features/WebMessaging/CamelCaseEnumWebMessageJsonConverter.cs index 28784c3fe..88a01f458 100644 --- a/src/InfiniFrame/Window/Features/WebMessaging/CamelCaseEnumWebMessageJsonConverter.cs +++ b/src/InfiniFrame.Window/Features/WebMessaging/CamelCaseEnumWebMessageJsonConverter.cs @@ -4,7 +4,7 @@ using System.Text.Json; using System.Text.Json.Serialization; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.WebMessaging; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/WebMessaging/Handlers/FullScreenWebMessageHandler.cs b/src/InfiniFrame.Window/Features/WebMessaging/Handlers/FullScreenWebMessageHandler.cs similarity index 93% rename from src/InfiniFrame/Window/Features/WebMessaging/Handlers/FullScreenWebMessageHandler.cs rename to src/InfiniFrame.Window/Features/WebMessaging/Handlers/FullScreenWebMessageHandler.cs index 07eb87110..9eb1f1163 100644 --- a/src/InfiniFrame/Window/Features/WebMessaging/Handlers/FullScreenWebMessageHandler.cs +++ b/src/InfiniFrame.Window/Features/WebMessaging/Handlers/FullScreenWebMessageHandler.cs @@ -1,9 +1,10 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame.Interop; +using InfiniFrame.Js.Interop; +using InfiniFrame.Window.Interop; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.WebMessaging.Handlers; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/WebMessaging/Handlers/GetWebMessageHandler.cs b/src/InfiniFrame.Window/Features/WebMessaging/Handlers/GetWebMessageHandler.cs similarity index 94% rename from src/InfiniFrame/Window/Features/WebMessaging/Handlers/GetWebMessageHandler.cs rename to src/InfiniFrame.Window/Features/WebMessaging/Handlers/GetWebMessageHandler.cs index 702413f2f..c70961b90 100644 --- a/src/InfiniFrame/Window/Features/WebMessaging/Handlers/GetWebMessageHandler.cs +++ b/src/InfiniFrame.Window/Features/WebMessaging/Handlers/GetWebMessageHandler.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.WebMessaging.Handlers; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/WebMessaging/Handlers/OpenExternalTargetWebMessageHandler.cs b/src/InfiniFrame.Window/Features/WebMessaging/Handlers/OpenExternalTargetWebMessageHandler.cs similarity index 97% rename from src/InfiniFrame/Window/Features/WebMessaging/Handlers/OpenExternalTargetWebMessageHandler.cs rename to src/InfiniFrame.Window/Features/WebMessaging/Handlers/OpenExternalTargetWebMessageHandler.cs index 49b0b7d5b..48abc4be0 100644 --- a/src/InfiniFrame/Window/Features/WebMessaging/Handlers/OpenExternalTargetWebMessageHandler.cs +++ b/src/InfiniFrame.Window/Features/WebMessaging/Handlers/OpenExternalTargetWebMessageHandler.cs @@ -4,12 +4,13 @@ using System.ComponentModel; using System.Diagnostics; using System.Net; -using InfiniFrame.Interop; +using InfiniFrame.Js.Interop; using InfiniFrame.Security; +using InfiniFrame.Window.Interop; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.WebMessaging.Handlers; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/WebMessaging/Handlers/TitleChangedWebMessageHandler.cs b/src/InfiniFrame.Window/Features/WebMessaging/Handlers/TitleChangedWebMessageHandler.cs similarity index 92% rename from src/InfiniFrame/Window/Features/WebMessaging/Handlers/TitleChangedWebMessageHandler.cs rename to src/InfiniFrame.Window/Features/WebMessaging/Handlers/TitleChangedWebMessageHandler.cs index 9ba998f86..8ff1e3233 100644 --- a/src/InfiniFrame/Window/Features/WebMessaging/Handlers/TitleChangedWebMessageHandler.cs +++ b/src/InfiniFrame.Window/Features/WebMessaging/Handlers/TitleChangedWebMessageHandler.cs @@ -1,9 +1,10 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame.Interop; +using InfiniFrame.Js.Interop; +using InfiniFrame.Window.Interop; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.WebMessaging.Handlers; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/WebMessaging/Handlers/WindowFeatureWebMessageDispatcherBase.cs b/src/InfiniFrame.Window/Features/WebMessaging/Handlers/WindowFeatureWebMessageDispatcherBase.cs similarity index 98% rename from src/InfiniFrame/Window/Features/WebMessaging/Handlers/WindowFeatureWebMessageDispatcherBase.cs rename to src/InfiniFrame.Window/Features/WebMessaging/Handlers/WindowFeatureWebMessageDispatcherBase.cs index b1333eb17..e5107433c 100644 --- a/src/InfiniFrame/Window/Features/WebMessaging/Handlers/WindowFeatureWebMessageDispatcherBase.cs +++ b/src/InfiniFrame.Window/Features/WebMessaging/Handlers/WindowFeatureWebMessageDispatcherBase.cs @@ -4,7 +4,7 @@ using System.Text.Json; using System.Text.Json.Serialization.Metadata; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.WebMessaging.Handlers; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/WebMessaging/Handlers/WindowFeatureWebMessageHandler.cs b/src/InfiniFrame.Window/Features/WebMessaging/Handlers/WindowFeatureWebMessageHandler.cs similarity index 97% rename from src/InfiniFrame/Window/Features/WebMessaging/Handlers/WindowFeatureWebMessageHandler.cs rename to src/InfiniFrame.Window/Features/WebMessaging/Handlers/WindowFeatureWebMessageHandler.cs index b21eaf2cb..ee881c4c9 100644 --- a/src/InfiniFrame/Window/Features/WebMessaging/Handlers/WindowFeatureWebMessageHandler.cs +++ b/src/InfiniFrame.Window/Features/WebMessaging/Handlers/WindowFeatureWebMessageHandler.cs @@ -2,9 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using System.Text.Json; -using InfiniFrame.Interop; +using InfiniFrame.Js.Interop; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.WebMessaging.Handlers; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/WebMessaging/Handlers/WindowFeatureWebMessageRouter.cs b/src/InfiniFrame.Window/Features/WebMessaging/Handlers/WindowFeatureWebMessageRouter.cs similarity index 82% rename from src/InfiniFrame/Window/Features/WebMessaging/Handlers/WindowFeatureWebMessageRouter.cs rename to src/InfiniFrame.Window/Features/WebMessaging/Handlers/WindowFeatureWebMessageRouter.cs index 22508b4a7..872340d43 100644 --- a/src/InfiniFrame/Window/Features/WebMessaging/Handlers/WindowFeatureWebMessageRouter.cs +++ b/src/InfiniFrame.Window/Features/WebMessaging/Handlers/WindowFeatureWebMessageRouter.cs @@ -3,8 +3,21 @@ // --------------------------------------------------------------------------------------------------------------------- using System.Text.Json; using System.Text.Json.Serialization.Metadata; +using InfiniFrame.Window.Features.Browser; +using InfiniFrame.Window.Features.Debugging; +using InfiniFrame.Window.Features.Decorations; +using InfiniFrame.Window.Features.FilePickerDialogs; +using InfiniFrame.Window.Features.Invoke; +using InfiniFrame.Window.Features.JavaScript; +using InfiniFrame.Window.Features.Lifecycle; +using InfiniFrame.Window.Features.Monitors; +using InfiniFrame.Window.Features.Notifications; +using InfiniFrame.Window.Features.PageNavigation; +using InfiniFrame.Window.Features.Position; +using InfiniFrame.Window.Features.Size; +using InfiniFrame.Window.Features.State; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.WebMessaging.Handlers; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/WebMessaging/Handlers/WindowManagementWebMessageHandler.cs b/src/InfiniFrame.Window/Features/WebMessaging/Handlers/WindowManagementWebMessageHandler.cs similarity index 97% rename from src/InfiniFrame/Window/Features/WebMessaging/Handlers/WindowManagementWebMessageHandler.cs rename to src/InfiniFrame.Window/Features/WebMessaging/Handlers/WindowManagementWebMessageHandler.cs index b07d57833..ee5eb2b92 100644 --- a/src/InfiniFrame/Window/Features/WebMessaging/Handlers/WindowManagementWebMessageHandler.cs +++ b/src/InfiniFrame.Window/Features/WebMessaging/Handlers/WindowManagementWebMessageHandler.cs @@ -2,9 +2,10 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using System.Text.Json; -using InfiniFrame.Interop; +using InfiniFrame.Js.Interop; +using InfiniFrame.Window.Interop; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.WebMessaging.Handlers; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/WebMessaging/WebMessagingInfiniFrameWindowFeature.cs b/src/InfiniFrame.Window/Features/WebMessaging/WebMessagingInfiniFrameWindowFeature.cs similarity index 98% rename from src/InfiniFrame/Window/Features/WebMessaging/WebMessagingInfiniFrameWindowFeature.cs rename to src/InfiniFrame.Window/Features/WebMessaging/WebMessagingInfiniFrameWindowFeature.cs index 6bd28c255..bc057dc4c 100644 --- a/src/InfiniFrame/Window/Features/WebMessaging/WebMessagingInfiniFrameWindowFeature.cs +++ b/src/InfiniFrame.Window/Features/WebMessaging/WebMessagingInfiniFrameWindowFeature.cs @@ -4,12 +4,12 @@ using System.Collections.Concurrent; using System.Text; using System.Text.Json; -using InfiniFrame.Interop; +using InfiniFrame.Js.Interop; using InfiniFrame.NativeBridge; using InfiniFrame.NativeBridge.Handles; using Microsoft.Extensions.Logging; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.WebMessaging; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/WebMessaging/WebMessagingWebMessageDispatcher.cs b/src/InfiniFrame.Window/Features/WebMessaging/WebMessagingWebMessageDispatcher.cs similarity index 91% rename from src/InfiniFrame/Window/Features/WebMessaging/WebMessagingWebMessageDispatcher.cs rename to src/InfiniFrame.Window/Features/WebMessaging/WebMessagingWebMessageDispatcher.cs index 602cf45a8..29055a79c 100644 --- a/src/InfiniFrame/Window/Features/WebMessaging/WebMessagingWebMessageDispatcher.cs +++ b/src/InfiniFrame.Window/Features/WebMessaging/WebMessagingWebMessageDispatcher.cs @@ -2,8 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using System.Text.Json; +using InfiniFrame.Window.Features.WebMessaging.Handlers; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.WebMessaging; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/Features/WebMessaging/WindowFeatureDrawingJsonConverters.cs b/src/InfiniFrame.Window/Features/WebMessaging/WindowFeatureDrawingJsonConverters.cs similarity index 90% rename from src/InfiniFrame/Window/Features/WebMessaging/WindowFeatureDrawingJsonConverters.cs rename to src/InfiniFrame.Window/Features/WebMessaging/WindowFeatureDrawingJsonConverters.cs index cdde1a605..590ac160c 100644 --- a/src/InfiniFrame/Window/Features/WebMessaging/WindowFeatureDrawingJsonConverters.cs +++ b/src/InfiniFrame.Window/Features/WebMessaging/WindowFeatureDrawingJsonConverters.cs @@ -5,7 +5,7 @@ using System.Text.Json; using System.Text.Json.Serialization; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.WebMessaging; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -35,16 +35,16 @@ internal static int RequiredInt(JsonElement value, string propertyName) { } } -internal sealed class SizeWebMessageJsonConverter : JsonConverter { - public override Size Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { +internal sealed class SizeWebMessageJsonConverter : JsonConverter { + public override System.Drawing.Size Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { using JsonDocument document = JsonDocument.ParseValue(ref reader); JsonElement value = document.RootElement; - return new Size( + return new System.Drawing.Size( PointWebMessageJsonConverter.RequiredInt(value, "width"), PointWebMessageJsonConverter.RequiredInt(value, "height")); } - public override void Write(Utf8JsonWriter writer, Size value, JsonSerializerOptions options) { + public override void Write(Utf8JsonWriter writer, System.Drawing.Size value, JsonSerializerOptions options) { writer.WriteStartObject(); writer.WriteNumber("width", value.Width); writer.WriteNumber("height", value.Height); diff --git a/src/InfiniFrame/Window/Features/WebMessaging/WindowFeatureWebMessageJsonContext.cs b/src/InfiniFrame.Window/Features/WebMessaging/WindowFeatureWebMessageJsonContext.cs similarity index 92% rename from src/InfiniFrame/Window/Features/WebMessaging/WindowFeatureWebMessageJsonContext.cs rename to src/InfiniFrame.Window/Features/WebMessaging/WindowFeatureWebMessageJsonContext.cs index 80f9f63f0..f9d154c0d 100644 --- a/src/InfiniFrame/Window/Features/WebMessaging/WindowFeatureWebMessageJsonContext.cs +++ b/src/InfiniFrame.Window/Features/WebMessaging/WindowFeatureWebMessageJsonContext.cs @@ -6,8 +6,10 @@ using System.Text.Json.Serialization; using InfiniFrame.Debugging; using InfiniFrame.NativeBridge.Dialogs; +using InfiniFrame.Window.Features.Debugging; +using InfiniFrame.Window.Features.FilePickerDialogs; -namespace InfiniFrame; +namespace InfiniFrame.Window.Features.WebMessaging; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -32,7 +34,7 @@ namespace InfiniFrame; [JsonSerializable(typeof(InfiniMonitor))] [JsonSerializable(typeof(InfiniMonitor[]))] [JsonSerializable(typeof(Point))] -[JsonSerializable(typeof(Size))] +[JsonSerializable(typeof(System.Drawing.Size))] [JsonSerializable(typeof(Rectangle))] [JsonSerializable(typeof(InfiniFrameDebugCapabilities))] [JsonSerializable(typeof(InfiniFrameDebugDiagnostics))] diff --git a/src/InfiniFrame.Window/InfiniFrame.Window.csproj b/src/InfiniFrame.Window/InfiniFrame.Window.csproj new file mode 100644 index 000000000..17f8ff978 --- /dev/null +++ b/src/InfiniFrame.Window/InfiniFrame.Window.csproj @@ -0,0 +1,21 @@ + + + InfiniLore.InfiniFrame.Window + InfiniFrame concrete window runtime, lifecycle, configuration, events, and features. + + + + + + + + + + + + + + + + + diff --git a/src/InfiniFrame/Window/InfiniFrameWindow.cs b/src/InfiniFrame.Window/InfiniFrameWindow.cs similarity index 89% rename from src/InfiniFrame/Window/InfiniFrameWindow.cs rename to src/InfiniFrame.Window/InfiniFrameWindow.cs index d5b0015ad..99a1a4f60 100644 --- a/src/InfiniFrame/Window/InfiniFrameWindow.cs +++ b/src/InfiniFrame.Window/InfiniFrameWindow.cs @@ -8,7 +8,7 @@ using InfiniFrame.NativeBridge.Handles; using Microsoft.Extensions.Logging; -namespace InfiniFrame; +namespace InfiniFrame.Window; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -27,17 +27,11 @@ public sealed class InfiniFrameWindow( private int _closeReturnState = (int)InfiniFrameWindowLifecycleState.Ready; private int _managedThreadId = Environment.CurrentManagedThreadId; private long _lastLifecycleTransitionUtcTicks = DateTimeOffset.UtcNow.UtcTicks; - private int _asyncDisposing; private readonly object _diagnosticsLock = new(); private readonly Dictionary _outstandingOperations = []; private InfiniFrameOperationDiagnostics? _lastOperation; private bool _ownsServiceProvider; - #if NET9_0_OR_GREATER - private readonly Lock _disposeLock = new(); - #else - // ReSharper disable once ConvertToAutoPropertyWhenPossible - private readonly object _disposeLock = new(); - #endif + private readonly SemaphoreSlim _disposeLock = new(1, 1); /// public IntPtr MainProgramHandle => LazyMainProgramHandle.Value; @@ -304,7 +298,9 @@ public NativeHandleLease AcquireNativeHandle(NativeHandleAccess access = NativeH } public void Dispose() { - lock (_disposeLock) { + _disposeLock.Wait(); + IServiceProvider? ownedProvider = null; + try { if (LifecycleState == InfiniFrameWindowLifecycleState.Disposed) return; if (!Features.Lifecycle.IsClosedOrClosing()) { @@ -327,28 +323,45 @@ public void Dispose() { Features.Lifecycle.CleanupNativeHandle(); - if (_ownsServiceProvider && ServiceProvider is IDisposable disposableProvider) { - disposableProvider.Dispose(); - } + if (_ownsServiceProvider) ownedProvider = ServiceProvider; + } + finally { + DetachFromParent(); + _disposeLock.Release(); } + if (ownedProvider is IDisposable disposable) + disposable.Dispose(); + else if (ownedProvider is IAsyncDisposable asyncDisposable) + asyncDisposable.DisposeAsync().AsTask().GetAwaiter().GetResult(); } public async ValueTask DisposeAsync() { - lock (_disposeLock) { - if (Interlocked.CompareExchange(ref _asyncDisposing, 1, 0) != 0) return; - if (LifecycleState == InfiniFrameWindowLifecycleState.Disposed) return; - } - + await _disposeLock.WaitAsync().ConfigureAwait(false); + IServiceProvider? ownedProvider = null; try { - if (!Features.Lifecycle.IsClosedOrClosing()) await Features.Lifecycle.CloseAsync().ConfigureAwait(false); - await Features.Lifecycle.WaitForTeardownAsync().ConfigureAwait(false); - } - finally { - Features.Lifecycle.CleanupNativeHandle(); + try { + if (LifecycleState == InfiniFrameWindowLifecycleState.Disposed) return; + if (!Features.Lifecycle.IsClosedOrClosing()) await Features.Lifecycle.CloseAsync().ConfigureAwait(false); + await Features.Lifecycle.WaitForTeardownAsync().ConfigureAwait(false); + } + finally { + Features.Lifecycle.CleanupNativeHandle(); - if (_ownsServiceProvider && ServiceProvider is IDisposable disposableProvider) { - using IDisposable _ = disposableProvider; + if (_ownsServiceProvider) ownedProvider = ServiceProvider; } } + finally { + DetachFromParent(); + _disposeLock.Release(); + } + if (ownedProvider is IAsyncDisposable asyncDisposable) + await asyncDisposable.DisposeAsync().ConfigureAwait(false); + else (ownedProvider as IDisposable)?.Dispose(); + } + + private void DetachFromParent() { + if (Configuration.ParentWindow?.Configuration is not InfiniFrameWindowConfiguration parentConfiguration) return; + lock (parentConfiguration.ChildWindowsLock) + parentConfiguration.ChildWindowsInternal.Remove(this); } } diff --git a/src/InfiniFrame/Window/InfiniFrameWindowConfiguration.cs b/src/InfiniFrame.Window/InfiniFrameWindowConfiguration.cs similarity index 88% rename from src/InfiniFrame/Window/InfiniFrameWindowConfiguration.cs rename to src/InfiniFrame.Window/InfiniFrameWindowConfiguration.cs index 492e09e4e..1b99ce24b 100644 --- a/src/InfiniFrame/Window/InfiniFrameWindowConfiguration.cs +++ b/src/InfiniFrame.Window/InfiniFrameWindowConfiguration.cs @@ -1,9 +1,9 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; -namespace InfiniFrame; +namespace InfiniFrame.Window; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -23,7 +23,7 @@ public class InfiniFrameWindowConfiguration : IInfiniFrameWindowConfiguration { /// internal object ChildWindowsLock { get; } = new(); /// - public InfiniFrameNativeParameters StartupParameters { get; private set; } + public InfiniFrameNativeWindowParameters StartupParameters { get; private set; } /// public IInfiniFrameWindow? ParentWindow { get; set; } /// @@ -33,7 +33,7 @@ public class InfiniFrameWindowConfiguration : IInfiniFrameWindowConfiguration { // Methods // ----------------------------------------------------------------------------------------------------------------- /// - public void AssignNativeParameters(InfiniFrameNativeParameters nativeParameters) { + public void AssignNativeParameters(InfiniFrameNativeWindowParameters nativeParameters) { StartupParameters = nativeParameters; } } diff --git a/src/InfiniFrame/Window/InfiniFrameWindowFeatures.cs b/src/InfiniFrame.Window/InfiniFrameWindowFeatures.cs similarity index 98% rename from src/InfiniFrame/Window/InfiniFrameWindowFeatures.cs rename to src/InfiniFrame.Window/InfiniFrameWindowFeatures.cs index 22bca5d07..ae8638252 100644 --- a/src/InfiniFrame/Window/InfiniFrameWindowFeatures.cs +++ b/src/InfiniFrame.Window/InfiniFrameWindowFeatures.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -namespace InfiniFrame; +namespace InfiniFrame.Window; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Window/InfiniFrameWindowFeaturesFactory.cs b/src/InfiniFrame.Window/InfiniFrameWindowFeaturesFactory.cs similarity index 85% rename from src/InfiniFrame/Window/InfiniFrameWindowFeaturesFactory.cs rename to src/InfiniFrame.Window/InfiniFrameWindowFeaturesFactory.cs index 7068e56b9..cdd2ba28d 100644 --- a/src/InfiniFrame/Window/InfiniFrameWindowFeaturesFactory.cs +++ b/src/InfiniFrame.Window/InfiniFrameWindowFeaturesFactory.cs @@ -2,11 +2,28 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using FluentValidation; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Features.Browser; +using InfiniFrame.Window.Features.Debugging; +using InfiniFrame.Window.Features.Decorations; +using InfiniFrame.Window.Features.DragDrop; +using InfiniFrame.Window.Features.FilePickerDialogs; +using InfiniFrame.Window.Features.Invoke; +using InfiniFrame.Window.Features.JavaScript; +using InfiniFrame.Window.Features.Lifecycle; +using InfiniFrame.Window.Features.Menu; +using InfiniFrame.Window.Features.Monitors; +using InfiniFrame.Window.Features.Notifications; +using InfiniFrame.Window.Features.PageNavigation; +using InfiniFrame.Window.Features.Position; +using InfiniFrame.Window.Features.Size; +using InfiniFrame.Window.Features.State; +using InfiniFrame.Window.Features.Taskbar; +using InfiniFrame.Window.Features.WebMessaging; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -namespace InfiniFrame; +namespace InfiniFrame.Window; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -42,7 +59,7 @@ public IInfiniFrameWindowFeatures Create(IInfiniFrameWindow window, IInfiniFrame new LifecycleInfiniFrameWindowFeature( window, GetLogger(provider), - provider.GetRequiredService>() + provider.GetRequiredService>() ), new InvokeInfiniFrameWindowFeature( window, diff --git a/src/InfiniFrame/Interop/ExternalProcessLauncher.cs b/src/InfiniFrame.Window/Interop/ExternalProcessLauncher.cs similarity index 95% rename from src/InfiniFrame/Interop/ExternalProcessLauncher.cs rename to src/InfiniFrame.Window/Interop/ExternalProcessLauncher.cs index 3340d9319..81bb5503d 100644 --- a/src/InfiniFrame/Interop/ExternalProcessLauncher.cs +++ b/src/InfiniFrame.Window/Interop/ExternalProcessLauncher.cs @@ -3,7 +3,7 @@ // --------------------------------------------------------------------------------------------------------------------- using System.Diagnostics; -namespace InfiniFrame.Interop; +namespace InfiniFrame.Window.Interop; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame.Shared/Interop/IExternalProcessLauncher.cs b/src/InfiniFrame.Window/Interop/IExternalProcessLauncher.cs similarity index 96% rename from src/InfiniFrame.Shared/Interop/IExternalProcessLauncher.cs rename to src/InfiniFrame.Window/Interop/IExternalProcessLauncher.cs index 33da38e8c..40b4c6f8b 100644 --- a/src/InfiniFrame.Shared/Interop/IExternalProcessLauncher.cs +++ b/src/InfiniFrame.Window/Interop/IExternalProcessLauncher.cs @@ -3,7 +3,7 @@ // --------------------------------------------------------------------------------------------------------------------- using System.Diagnostics; -namespace InfiniFrame.Interop; +namespace InfiniFrame.Window.Interop; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Interop/RegisterWindowCreatedUtility.cs b/src/InfiniFrame.Window/Interop/RegisterWindowCreatedUtility.cs similarity index 98% rename from src/InfiniFrame/Interop/RegisterWindowCreatedUtility.cs rename to src/InfiniFrame.Window/Interop/RegisterWindowCreatedUtility.cs index f2f47fc18..0d556cdc5 100644 --- a/src/InfiniFrame/Interop/RegisterWindowCreatedUtility.cs +++ b/src/InfiniFrame.Window/Interop/RegisterWindowCreatedUtility.cs @@ -2,10 +2,11 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using System.Runtime.CompilerServices; +using InfiniFrame.Js.Interop; using InfiniFrame.Utilities; using Microsoft.Extensions.Logging; -namespace InfiniFrame.Interop; +namespace InfiniFrame.Window.Interop; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Interop/WindowReadyRegistrationState.cs b/src/InfiniFrame.Window/Interop/WindowReadyRegistrationState.cs similarity index 98% rename from src/InfiniFrame/Interop/WindowReadyRegistrationState.cs rename to src/InfiniFrame.Window/Interop/WindowReadyRegistrationState.cs index b4083ec6a..f73c3d496 100644 --- a/src/InfiniFrame/Interop/WindowReadyRegistrationState.cs +++ b/src/InfiniFrame.Window/Interop/WindowReadyRegistrationState.cs @@ -3,7 +3,7 @@ // --------------------------------------------------------------------------------------------------------------------- using System.Runtime.CompilerServices; -namespace InfiniFrame.Interop; +namespace InfiniFrame.Window.Interop; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Interop/WindowRegistrationHandshakeState.cs b/src/InfiniFrame.Window/Interop/WindowRegistrationHandshakeState.cs similarity index 94% rename from src/InfiniFrame/Interop/WindowRegistrationHandshakeState.cs rename to src/InfiniFrame.Window/Interop/WindowRegistrationHandshakeState.cs index 7ba8ba4e3..c2ea2ffbc 100644 --- a/src/InfiniFrame/Interop/WindowRegistrationHandshakeState.cs +++ b/src/InfiniFrame.Window/Interop/WindowRegistrationHandshakeState.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -namespace InfiniFrame.Interop; +namespace InfiniFrame.Window.Interop; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Interop/WindowRegistrationState.cs b/src/InfiniFrame.Window/Interop/WindowRegistrationState.cs similarity index 95% rename from src/InfiniFrame/Interop/WindowRegistrationState.cs rename to src/InfiniFrame.Window/Interop/WindowRegistrationState.cs index 3d805088b..41f8e9e3d 100644 --- a/src/InfiniFrame/Interop/WindowRegistrationState.cs +++ b/src/InfiniFrame.Window/Interop/WindowRegistrationState.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -namespace InfiniFrame.Interop; +namespace InfiniFrame.Window.Interop; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/Interop/WindowRegistrationStateMachine.cs b/src/InfiniFrame.Window/Interop/WindowRegistrationStateMachine.cs similarity index 98% rename from src/InfiniFrame/Interop/WindowRegistrationStateMachine.cs rename to src/InfiniFrame.Window/Interop/WindowRegistrationStateMachine.cs index 773674019..b6bde14c4 100644 --- a/src/InfiniFrame/Interop/WindowRegistrationStateMachine.cs +++ b/src/InfiniFrame.Window/Interop/WindowRegistrationStateMachine.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -namespace InfiniFrame.Interop; +namespace InfiniFrame.Window.Interop; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/src/InfiniFrame/ServiceCollectionExtensions.cs b/src/InfiniFrame.Window/ServiceCollectionExtensions.cs similarity index 76% rename from src/InfiniFrame/ServiceCollectionExtensions.cs rename to src/InfiniFrame.Window/ServiceCollectionExtensions.cs index 381016667..184a1a9f9 100644 --- a/src/InfiniFrame/ServiceCollectionExtensions.cs +++ b/src/InfiniFrame.Window/ServiceCollectionExtensions.cs @@ -2,11 +2,12 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using FluentValidation; -using InfiniFrame.Interop; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Events; +using InfiniFrame.Window.Interop; using Microsoft.Extensions.DependencyInjection; -namespace InfiniFrame; +namespace InfiniFrame.Window; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -20,11 +21,11 @@ public static class ServiceCollectionExtensions { /// The to add services to. /// The same service collection so calls can be chained. public static IServiceCollection AddInfiniFrame(this IServiceCollection services) { - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); services.AddTransient(); - services.AddSingleton, InfiniFrameNativeParametersValidator>(); + services.AddSingleton, InfiniFrameNativeWindowParametersValidator>(); services.AddSingleton(); services.AddSingleton(); diff --git a/src/InfiniFrame/InfiniFrame.csproj b/src/InfiniFrame/InfiniFrame.csproj index 74053c8d7..e3bf819db 100644 --- a/src/InfiniFrame/InfiniFrame.csproj +++ b/src/InfiniFrame/InfiniFrame.csproj @@ -7,21 +7,19 @@ - - - - - - + + + + + + + - - - - - - + + + diff --git a/src/InfiniFrame/Window/Builder/InfiniFrameWindowBuilder.cs b/src/InfiniFrame/Window/Builder/InfiniFrameWindowBuilder.cs deleted file mode 100644 index e79a0058c..000000000 --- a/src/InfiniFrame/Window/Builder/InfiniFrameWindowBuilder.cs +++ /dev/null @@ -1,105 +0,0 @@ -// --------------------------------------------------------------------------------------------------------------------- -// Imports -// --------------------------------------------------------------------------------------------------------------------- -using FluentValidation; -using InfiniFrame.NativeBridge.Parameters; -using InfiniFrame.Security; -using Microsoft.Extensions.DependencyInjection; - -namespace InfiniFrame; -// --------------------------------------------------------------------------------------------------------------------- -// Code -// --------------------------------------------------------------------------------------------------------------------- -/// -/// Default implementation of that collects configuration, -/// features, and event handlers to construct an . -/// -public class InfiniFrameWindowBuilder : IInfiniFrameWindowBuilder { - - private IServiceCollection Services { get; init; } = new ServiceCollection().AddInfiniFrame(); - /// - public IInfiniFrameWindowBuilderConfiguration Configuration { get; } = new InfiniFrameWindowBuilderConfiguration(); - /// - public IInfiniFrameWindowBuilderFeatures Features { get; } = new InfiniFrameWindowBuilderFeatures(); - /// - public IDebuggingInfiniFrameWindowBuilderFeature Debugging => Features.Debugging; - /// - public IInfiniFrameEventsStore EventsStore { get; private init; } = new InfiniFrameEventsStore(); - - /// - public IInfiniFrameStaticAssets? StaticAssets { get; set; } - - // ----------------------------------------------------------------------------------------------------------------- - // Methods - // ----------------------------------------------------------------------------------------------------------------- - /// - public IInfiniFrameWindow Build(IServiceProvider? provider = null) { - bool ownsServiceProvider = provider is null; - IServiceProvider actualProvider = provider ?? Services.BuildServiceProvider(); - var featureFactory = actualProvider.GetRequiredService(); - var validator = actualProvider.GetRequiredService>(); - - InfiniFrameNativeParameters nativeParameters = CollectNativeParameters(); - - // Instance arbitration check - IInstanceArbitrationInfiniFrameWindowBuilderFeature arbitration = Features.InstanceArbitration; - if (arbitration.Mode != InstanceArbitrationMode.Disabled - && !InstanceArbitration.TryAcquirePrimaryInstance(arbitration.MutexName)) { - throw new InstanceAlreadyRunningException(); - } - - var window = actualProvider.GetRequiredService(); - window.SetOwnsServiceProvider(ownsServiceProvider); - - window.AssignFeatures(featureFactory.Create(window, this)); - - window.Events.PopulateFromBuilderEventStore(EventsStore); - window.Events.AssignToNativeParameters(ref nativeParameters); - window.Events.AssignDefaultEventCallbacks(); - window.Events.AssignToWindow(window); - - window.Configuration.ParentWindow = Configuration.ParentWindow; - window.Configuration.AssignNativeParameters(nativeParameters); - - InfiniFrameUriSecurityPolicyRegistry.BindToWindow( - window, - InfiniFrameUriSecurityPolicyRegistry.GetForBuilder(this) - ); - - validator.ValidateAndThrow(nativeParameters); - - window.Features.Lifecycle.Initialize(); - - return window; - - } - - // ----------------------------------------------------------------------------------------------------------------- - // Constructors - // ----------------------------------------------------------------------------------------------------------------- - /// - /// Creates a new with optional DI and event store overrides. - /// - /// Optional service collection. If null, a default collection with logging and InfiniFrame core services is created. - /// Optional pre-configured event store. If null, a new empty store is created. - /// A configured ready for feature configuration. - public static InfiniFrameWindowBuilder Create(IServiceCollection? collection = null, InfiniFrameEventsStore? events = null) { - var builder = new InfiniFrameWindowBuilder { - EventsStore = events ?? new InfiniFrameEventsStore(), - Services = (collection ?? new ServiceCollection()) - .AddLogging() - .AddInfiniFrame() - }; - - return builder; - } - - internal InfiniFrameNativeParameters CollectNativeParameters() { - var parameters = new InfiniFrameNativeParameters(); - - Configuration.ApplyToNativeParameters(ref parameters); - Features.ApplyToNativeParameters(ref parameters); - - return parameters; - } -} diff --git a/tests/InfiniAutomationTests.BlazorWebView.MudBlazor/CustomElementsTests.cs b/tests/InfiniAutomationTests.BlazorWebView.MudBlazor/CustomElementsTests.cs index 28e9641ce..436c953bf 100644 --- a/tests/InfiniAutomationTests.BlazorWebView.MudBlazor/CustomElementsTests.cs +++ b/tests/InfiniAutomationTests.BlazorWebView.MudBlazor/CustomElementsTests.cs @@ -3,7 +3,7 @@ // --------------------------------------------------------------------------------------------------------------------- using InfiniAutomationTests.BlazorWebView.MudBlazor.TestUtility; using InfiniAutomationTests.Tests; -using InfiniTests; +using InfiniTests.TestSupport.Attributes; using Microsoft.Playwright; namespace InfiniAutomationTests.BlazorWebView.MudBlazor; diff --git a/tests/InfiniAutomationTests.BlazorWebView.MudBlazor/CustomSchemeCorsHeaderTests.cs b/tests/InfiniAutomationTests.BlazorWebView.MudBlazor/CustomSchemeCorsHeaderTests.cs index 925ee6134..0ad608b4c 100644 --- a/tests/InfiniAutomationTests.BlazorWebView.MudBlazor/CustomSchemeCorsHeaderTests.cs +++ b/tests/InfiniAutomationTests.BlazorWebView.MudBlazor/CustomSchemeCorsHeaderTests.cs @@ -4,7 +4,7 @@ using System.Text.Json; using InfiniAutomationTests.BlazorWebView.MudBlazor.TestUtility; using InfiniAutomationTests.Tests; -using InfiniTests; +using InfiniTests.TestSupport.Attributes; using Microsoft.Playwright; namespace InfiniAutomationTests.BlazorWebView.MudBlazor; diff --git a/tests/InfiniAutomationTests.BlazorWebView.MudBlazor/FragmentAndCustomSchemeRequestTests.cs b/tests/InfiniAutomationTests.BlazorWebView.MudBlazor/FragmentAndCustomSchemeRequestTests.cs index 5b9a8fd83..94eda461b 100644 --- a/tests/InfiniAutomationTests.BlazorWebView.MudBlazor/FragmentAndCustomSchemeRequestTests.cs +++ b/tests/InfiniAutomationTests.BlazorWebView.MudBlazor/FragmentAndCustomSchemeRequestTests.cs @@ -4,7 +4,7 @@ using System.Text.Json; using InfiniAutomationTests.BlazorWebView.MudBlazor.TestUtility; using InfiniAutomationTests.Tests; -using InfiniTests; +using InfiniTests.TestSupport.Attributes; using Microsoft.Playwright; namespace InfiniAutomationTests.BlazorWebView.MudBlazor; diff --git a/tests/InfiniAutomationTests.BlazorWebView.MudBlazor/InfiniAutomationTests.BlazorWebView.MudBlazor.csproj b/tests/InfiniAutomationTests.BlazorWebView.MudBlazor/InfiniAutomationTests.BlazorWebView.MudBlazor.csproj index ad58b00e8..0b4c4f008 100644 --- a/tests/InfiniAutomationTests.BlazorWebView.MudBlazor/InfiniAutomationTests.BlazorWebView.MudBlazor.csproj +++ b/tests/InfiniAutomationTests.BlazorWebView.MudBlazor/InfiniAutomationTests.BlazorWebView.MudBlazor.csproj @@ -12,7 +12,7 @@ - + diff --git a/tests/InfiniAutomationTests.BlazorWebView.MudBlazor/ScriptSrcImportTests.cs b/tests/InfiniAutomationTests.BlazorWebView.MudBlazor/ScriptSrcImportTests.cs index 1b44ab260..919a9cba6 100644 --- a/tests/InfiniAutomationTests.BlazorWebView.MudBlazor/ScriptSrcImportTests.cs +++ b/tests/InfiniAutomationTests.BlazorWebView.MudBlazor/ScriptSrcImportTests.cs @@ -4,7 +4,7 @@ using System.Text.Json; using InfiniAutomationTests.BlazorWebView.MudBlazor.TestUtility; using InfiniAutomationTests.Tests; -using InfiniTests; +using InfiniTests.TestSupport.Attributes; using Microsoft.Playwright; namespace InfiniAutomationTests.BlazorWebView.MudBlazor; diff --git a/tests/InfiniAutomationTests.BlazorWebView.MudBlazor/TestSettings.cs b/tests/InfiniAutomationTests.BlazorWebView.MudBlazor/TestSettings.cs index c1add5034..76a426ae4 100644 --- a/tests/InfiniAutomationTests.BlazorWebView.MudBlazor/TestSettings.cs +++ b/tests/InfiniAutomationTests.BlazorWebView.MudBlazor/TestSettings.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniTests; +using InfiniTests.TestSupport.Attributes; // --------------------------------------------------------------------------------------------------------------------- // Code diff --git a/tests/InfiniAutomationTests.WebApp/InfiniAutomationTests.WebApp.csproj b/tests/InfiniAutomationTests.WebApp/InfiniAutomationTests.WebApp.csproj index a6f968420..69a24450f 100644 --- a/tests/InfiniAutomationTests.WebApp/InfiniAutomationTests.WebApp.csproj +++ b/tests/InfiniAutomationTests.WebApp/InfiniAutomationTests.WebApp.csproj @@ -6,7 +6,7 @@ - + diff --git a/tests/InfiniAutomationTests.WebApp/Shared/TestSettings.cs b/tests/InfiniAutomationTests.WebApp/Shared/TestSettings.cs index e09939b3b..0fe843c01 100644 --- a/tests/InfiniAutomationTests.WebApp/Shared/TestSettings.cs +++ b/tests/InfiniAutomationTests.WebApp/Shared/TestSettings.cs @@ -1,10 +1,10 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniTests; +using InfiniTests.TestSupport.Attributes; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- [assembly: DefaultInfiniTestsTimeout] [assembly: SkipOnLinux] -[assembly: SkipOnMacOs] \ No newline at end of file +[assembly: SkipOnMacOs] diff --git a/tests/InfiniAutomationTests.WebApp/TestUtility/ServerPlaywrightContextBase.cs b/tests/InfiniAutomationTests.WebApp/TestUtility/ServerPlaywrightContextBase.cs index 0b8dcb546..c933f00ac 100644 --- a/tests/InfiniAutomationTests.WebApp/TestUtility/ServerPlaywrightContextBase.cs +++ b/tests/InfiniAutomationTests.WebApp/TestUtility/ServerPlaywrightContextBase.cs @@ -3,7 +3,8 @@ // --------------------------------------------------------------------------------------------------------------------- using InfiniAutomationTests.TestUtility; using InfiniFrame; -using InfiniTests; +using InfiniFrame.Window.Features.WebMessaging.Handlers; +using InfiniTests.TestSupport; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Playwright; @@ -22,7 +23,27 @@ public abstract class ServerPlaywrightContextBase(string documentTitle) : Playwr private string ServerUrl => $"http://127.0.0.1:{_serverPort}"; protected void BeforeAll() - => StartUtilityWithFreshPorts(); + { + using var startupMutex = new Mutex(false, "InfiniFrameDesktopStartup"); + try { + startupMutex.WaitOne(); + } + catch (AbandonedMutexException) { + // The previous test process exited during desktop startup. + } + + try { + StartUtilityWithFreshPorts(); + + // Warm the CDP connection during assembly setup. WebView2 startup can be + // delayed under the full parallel solution workload; deferring the first + // connection to a test subjects it to the short per-test timeout. + GetOrCreateBrowserAsync().GetAwaiter().GetResult(); + } + finally { + startupMutex.ReleaseMutex(); + } + } protected async ValueTask AfterAllAsync() { BeforeAssemblyTeardown(); @@ -64,7 +85,7 @@ private void StartUtilityWithFreshPorts() { using var startupCancellation = new CancellationTokenSource(TimeSpan.FromSeconds(90)); _utility = InfiniFrameTestServer.Create( - appBuilder: serverBuilder => serverBuilder.WebHost.UseUrls(ServerUrl), + appBuilder: serverBuilder => serverBuilder.UseUrls(ServerUrl), windowBuilder: windowBuilder => { if (OperatingSystem.IsWindows() || OperatingSystem.IsLinux()) windowBuilder.Debugging.SetRemoteDebuggingPort(_playwrightDevtoolsPort); diff --git a/tests/InfiniAutomationTests.WebApp/Tests/SharedWebServerStartupTests.cs b/tests/InfiniAutomationTests.WebApp/Tests/SharedWebServerStartupTests.cs index b2a3636c8..d21eff7f7 100644 --- a/tests/InfiniAutomationTests.WebApp/Tests/SharedWebServerStartupTests.cs +++ b/tests/InfiniAutomationTests.WebApp/Tests/SharedWebServerStartupTests.cs @@ -2,7 +2,7 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniAutomationTests.Tests; -using InfiniTests; +using InfiniTests.TestSupport.Attributes; using Microsoft.Playwright; namespace InfiniAutomationTests.WebApp.Tests; diff --git a/tests/InfiniAutomationTests/InfiniAutomationTests.csproj b/tests/InfiniAutomationTests/InfiniAutomationTests.csproj index d6705ada0..bd4c8dc1b 100644 --- a/tests/InfiniAutomationTests/InfiniAutomationTests.csproj +++ b/tests/InfiniAutomationTests/InfiniAutomationTests.csproj @@ -7,7 +7,7 @@ - - + + diff --git a/tests/InfiniAutomationTests/TestSettings.cs b/tests/InfiniAutomationTests/TestSettings.cs index c1add5034..76a426ae4 100644 --- a/tests/InfiniAutomationTests/TestSettings.cs +++ b/tests/InfiniAutomationTests/TestSettings.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniTests; +using InfiniTests.TestSupport.Attributes; // --------------------------------------------------------------------------------------------------------------------- // Code diff --git a/tests/InfiniAutomationTests/TestUtility/BlazorPlaywrightContextBase.cs b/tests/InfiniAutomationTests/TestUtility/BlazorPlaywrightContextBase.cs index 9176f3883..ce3302b52 100644 --- a/tests/InfiniAutomationTests/TestUtility/BlazorPlaywrightContextBase.cs +++ b/tests/InfiniAutomationTests/TestUtility/BlazorPlaywrightContextBase.cs @@ -2,8 +2,10 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniFrame.Application; using InfiniFrame.BlazorWebView; using InfiniFrame.Utilities; +using InfiniFrame.Window.Features.WebMessaging.Handlers; using JetBrains.Annotations; using Microsoft.AspNetCore.Components; using Microsoft.Extensions.DependencyInjection; @@ -18,7 +20,6 @@ public abstract class BlazorPlaywrightContextBase(string documen private readonly int _playwrightDevtoolsPort = PlaywrightConnectionUtility.GetAvailablePort(); [UsedImplicitly] - private InfiniFrameBlazorApp? _app;// kept for future reference private Thread? _appThread; private IInfiniFrameWindow? _window; public override IInfiniFrameWindow Window => _window!; @@ -27,26 +28,36 @@ public abstract class BlazorPlaywrightContextBase(string documen // Methods // ----------------------------------------------------------------------------------------------------------------- protected async Task BeforeAllAsync() { - using var startupCancellation = new CancellationTokenSource(TimeSpan.FromSeconds(90)); + TimeSpan startupTimeout = TimeSpan.FromSeconds(90); + using var startupCancellation = new CancellationTokenSource(startupTimeout); var ready = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); _appThread = CreateAppThread(ready); _appThread.Start(); - await ready.Task.WaitAsync(startupCancellation.Token); + try { + await ready.Task.WaitAsync(startupCancellation.Token); + } + catch (OperationCanceledException) { + throw new TimeoutException( + $"The Blazor application did not create its window within {startupTimeout.TotalSeconds:0} seconds."); + } Uri cdpEndpoint = PlaywrightConnectionUtility.CreateCdpConnectionUrl(_playwrightDevtoolsPort); - Console.WriteLine($"[PlaywrightSetup] Waiting for CDP endpoint at {cdpEndpoint}..."); - using var probeCancellation = new CancellationTokenSource(TimeSpan.FromSeconds(60)); - while (!probeCancellation.Token.IsCancellationRequested) { + using var probeCancellation = CancellationTokenSource.CreateLinkedTokenSource(startupCancellation.Token); + while (true) { if (RemoteDebuggingUtility.TryProbeEndpoint(cdpEndpoint, out _)) { - Console.WriteLine($"[PlaywrightSetup] CDP endpoint at {cdpEndpoint} is reachable."); return; } - await Task.Delay(500, probeCancellation.Token).ConfigureAwait(false); + try { + await Task.Delay(500, probeCancellation.Token).ConfigureAwait(false); + } + catch (OperationCanceledException) { + throw new TimeoutException( + $"The Blazor CDP endpoint '{cdpEndpoint}' was not reachable within " + + $"{startupTimeout.TotalSeconds:0} seconds."); + } } - Console.WriteLine($"[PlaywrightSetup] WARNING: CDP endpoint at {cdpEndpoint} not reachable after 60s. " + - "Proceeding anyway — Playwright connection will retry."); } protected void AfterAll() { @@ -55,7 +66,6 @@ protected void AfterAll() { JoinAppThreadSafely(); - _app = null; _window = null; _appThread = null; } @@ -82,7 +92,7 @@ protected virtual void ConfigureWindowBuilder(IInfiniFrameWindowBuilder windowBu }); } - protected virtual void RunApp(InfiniFrameBlazorApp app) + protected virtual void RunApp(InfiniFrameApplication app) => app.Run(); private Thread CreateAppThread(TaskCompletionSource ready) { @@ -99,21 +109,21 @@ private Thread CreateAppThread(TaskCompletionSource ready) { private void RunAppOnThread(TaskCompletionSource ready) { try { - var builder = InfiniFrameBlazorAppBuilder.CreateDefault(); + InfiniFrameApplicationBuilder builder = InfiniFrameApplication.CreateBuilder(); ConfigureServices(builder.Services); - ConfigureRootComponents(builder.RootComponents); - builder.RootComponents.Add("app"); - builder.WithInfiniFrameWindowBuilder(windowBuilder => ConfigureWindowBuilder(windowBuilder, _playwrightDevtoolsPort)); - - InfiniFrameBlazorApp app = builder.Build(); - var window = app.ServiceProvider.GetRequiredService(); - - _app = app; - _window = window; - ready.SetResult(null); + builder.WithWindow(windowBuilder => ConfigureWindowBuilder(windowBuilder, _playwrightDevtoolsPort)); + builder.UseBlazorWebView(configuration => { + ConfigureRootComponents(configuration.RootComponents); + configuration.RootComponents.Add("app"); + }); - RunApp(app); + InfiniFrameApplication application = builder.Build(); + application.WindowCreated += window => { + _window = window; + ready.TrySetResult(null); + }; + RunApp(application); } catch (InvalidOperationException ex) { ready.TrySetException(ex); @@ -124,6 +134,9 @@ private void RunAppOnThread(TaskCompletionSource ready) { catch (PlaywrightException ex) { ready.TrySetException(ex); } + catch (Exception ex) { + ready.TrySetException(ex); + } } private void CloseWindowSafely() { diff --git a/tests/InfiniAutomationTests/Tests/SharedDataExchangeTests.cs b/tests/InfiniAutomationTests/Tests/SharedDataExchangeTests.cs index 2c61996ae..c0e9ce3eb 100644 --- a/tests/InfiniAutomationTests/Tests/SharedDataExchangeTests.cs +++ b/tests/InfiniAutomationTests/Tests/SharedDataExchangeTests.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniTests; +using InfiniTests.TestSupport.Attributes; using Microsoft.Playwright; namespace InfiniAutomationTests.Tests; diff --git a/tests/InfiniAutomationTests/Tests/SharedJavaScriptEvaluationTests.cs b/tests/InfiniAutomationTests/Tests/SharedJavaScriptEvaluationTests.cs index 07f9b5a40..b0e663a56 100644 --- a/tests/InfiniAutomationTests/Tests/SharedJavaScriptEvaluationTests.cs +++ b/tests/InfiniAutomationTests/Tests/SharedJavaScriptEvaluationTests.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniTests; +using InfiniTests.TestSupport.Attributes; using Microsoft.Playwright; namespace InfiniAutomationTests.Tests; diff --git a/tests/InfiniAutomationTests/Tests/SharedJavascriptInteropTests.cs b/tests/InfiniAutomationTests/Tests/SharedJavascriptInteropTests.cs index 29c049fe8..86600888c 100644 --- a/tests/InfiniAutomationTests/Tests/SharedJavascriptInteropTests.cs +++ b/tests/InfiniAutomationTests/Tests/SharedJavascriptInteropTests.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniTests; +using InfiniTests.TestSupport.Attributes; using Microsoft.Playwright; namespace InfiniAutomationTests.Tests; diff --git a/tests/InfiniAutomationTests/Tests/SharedJavascriptTests.cs b/tests/InfiniAutomationTests/Tests/SharedJavascriptTests.cs index d9b99a28e..07b6f097e 100644 --- a/tests/InfiniAutomationTests/Tests/SharedJavascriptTests.cs +++ b/tests/InfiniAutomationTests/Tests/SharedJavascriptTests.cs @@ -2,7 +2,7 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using System.Text.Json; -using InfiniTests; +using InfiniTests.TestSupport.Attributes; using Microsoft.Playwright; namespace InfiniAutomationTests.Tests; diff --git a/tests/InfiniAutomationTests/Tests/SharedWebviewWindowTests.cs b/tests/InfiniAutomationTests/Tests/SharedWebviewWindowTests.cs index d13727b29..faee8b547 100644 --- a/tests/InfiniAutomationTests/Tests/SharedWebviewWindowTests.cs +++ b/tests/InfiniAutomationTests/Tests/SharedWebviewWindowTests.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniTests; +using InfiniTests.TestSupport.Attributes; using Microsoft.Playwright; namespace InfiniAutomationTests.Tests; diff --git a/tests/InfiniAutomationTests/Tests/SharedWindowChromeTests.cs b/tests/InfiniAutomationTests/Tests/SharedWindowChromeTests.cs index 32152a379..d1714e472 100644 --- a/tests/InfiniAutomationTests/Tests/SharedWindowChromeTests.cs +++ b/tests/InfiniAutomationTests/Tests/SharedWindowChromeTests.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniTests; +using InfiniTests.TestSupport.Attributes; using Microsoft.Playwright; namespace InfiniAutomationTests.Tests; diff --git a/tests/InfiniAutomationTests/Tests/SharedWindowFeatureMirroringTests.cs b/tests/InfiniAutomationTests/Tests/SharedWindowFeatureMirroringTests.cs index 841a114bb..98ba9e306 100644 --- a/tests/InfiniAutomationTests/Tests/SharedWindowFeatureMirroringTests.cs +++ b/tests/InfiniAutomationTests/Tests/SharedWindowFeatureMirroringTests.cs @@ -4,7 +4,7 @@ using System.Drawing; using System.Text.Json; using InfiniFrame; -using InfiniTests; +using InfiniTests.TestSupport.Attributes; using Microsoft.Playwright; namespace InfiniAutomationTests.Tests; diff --git a/tests/InfiniAutomationTests/Tests/ZoomDpiParityAutomationTests.cs b/tests/InfiniAutomationTests/Tests/ZoomDpiParityAutomationTests.cs index 90f440e5a..9d04caad0 100644 --- a/tests/InfiniAutomationTests/Tests/ZoomDpiParityAutomationTests.cs +++ b/tests/InfiniAutomationTests/Tests/ZoomDpiParityAutomationTests.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniTests; +using InfiniTests.TestSupport.Attributes; using Microsoft.Playwright; namespace InfiniAutomationTests.Tests; diff --git a/tests/InfiniTests.InfiniFrame.Application/InfiniFrameApplicationTests.cs b/tests/InfiniTests.InfiniFrame.Application/InfiniFrameApplicationTests.cs new file mode 100644 index 000000000..65bd71c2c --- /dev/null +++ b/tests/InfiniTests.InfiniFrame.Application/InfiniFrameApplicationTests.cs @@ -0,0 +1,152 @@ +// --------------------------------------------------------------------------------------------------------------------- +// Imports +// --------------------------------------------------------------------------------------------------------------------- +using InfiniFrame; +using InfiniFrame.Application; +using InfiniTests.TestSupport.Attributes; + +namespace InfiniTests.InfiniFrame.Application; +// --------------------------------------------------------------------------------------------------------------------- +// Code +// --------------------------------------------------------------------------------------------------------------------- + +[NotInParallelInfiniTests] +public sealed class InfiniFrameApplicationTests { + [Test] + public async Task Initialize_CreatesApplicationWithNoWindows(CancellationToken ct = default) { + if (!OperatingSystem.IsWindows()) return; + + await using InfiniFrameApplication application = InfiniFrameApplication.Initialize(); + + await Assert.That(application.Windows).IsEmpty(); + } + + [Test] + public async Task CreateBuilder_RegistersUnnamedWindowWithoutIntegrationId(CancellationToken ct = default) { + if (!OperatingSystem.IsWindows()) return; + + await using InfiniFrameApplication application = InfiniFrameApplication.CreateBuilder() + .WithWindow(static window => window.SetStartPageContent("App")) + .Build(); + + await Assert.That(application.Windows).IsEmpty(); + } + + [Test] + public async Task RegisterWindow_DuplicateIdThrows(CancellationToken ct = default) { + if (!OperatingSystem.IsWindows()) return; + + await using InfiniFrameApplication application = InfiniFrameApplication.Initialize(); + application.RegisterWindow("main", configure: static _ => {}); + + // ReSharper disable once AccessToDisposedClosure + await Assert.That(() => application.RegisterWindow("main", configure: static _ => {})) + .Throws(); + } + + [Test] + public async Task LookupBeforeRunFailsClearly(CancellationToken ct = default) { + if (!OperatingSystem.IsWindows()) return; + + await using InfiniFrameApplication application = InfiniFrameApplication.Initialize(); + application.RegisterWindow("main", configure: static _ => {}); + + // ReSharper disable once AccessToDisposedClosure + await Assert.That(() => application.GetWindow("main")) + .Throws(); + await Assert.That(application.TryGetWindow("main")).IsNull(); + } + + [Test] + public async Task WebView2RuntimeConfigurationCanBeSetBeforeRun(CancellationToken ct = default) { + if (!OperatingSystem.IsWindows()) return; + + await using InfiniFrameApplication application = InfiniFrameApplication.CreateBuilder() + .WithWebView2RuntimePath(Environment.SystemDirectory) + .Build(); + + await Assert.That(application.Windows).IsEmpty(); + } + + [Test] + public async Task ProcessWideConfigurationCanBeSetBeforeRun(CancellationToken ct = default) { + if (!OperatingSystem.IsWindows()) return; + + await using InfiniFrameApplication application = InfiniFrameApplication.CreateBuilder() + .WithWebView2RuntimePath(Environment.SystemDirectory) + .WithNotificationRegistrationId("InfiniFrame.Tests") + .WithAppUserModelId("InfiniFrame.Tests") + .WithDefaultNotificationIcon(Environment.ProcessPath!) + .Build(); + + await Assert.That(application.Windows).IsEmpty(); + } + + [Test] + public async Task RegistrationAfterRunFails(CancellationToken ct = default) { + if (!OperatingSystem.IsWindows()) return; + + await using InfiniFrameApplication application = InfiniFrameApplication.Initialize(); + await application.RunAsync(ct); + + // ReSharper disable once AccessToDisposedClosure + await Assert.That(() => application.RegisterWindow(static _ => {})) + .Throws(); + } + + [Test] + public async Task RunFromMtaThreadFailsBeforeWindowCreation(CancellationToken ct = default) { + if (!OperatingSystem.IsWindows()) return; + + await using InfiniFrameApplication application = InfiniFrameApplication.Initialize(); + // ReSharper disable once AccessToDisposedClosure + await Assert.That(async () => await Task.Run(application.Run, ct)) + .Throws(); + } + + [Test] + [NotInParallelInfiniTests] + [Timeout(60_000)] + public async Task RunAsyncBuildsAndRunsMultipleWindowsUntilAllClose(CancellationToken ct = default) { + if (!OperatingSystem.IsWindows() || Environment.Version.Major < 10) return; + + await using InfiniFrameApplication application = InfiniFrameApplication.Initialize() + .WithWindow("main", configure: static builder => builder.SetStartPageContent("Main")) + .WithWindow("settings", configure: static builder => builder.SetStartPageContent("Settings")); + + Task runTask = application.RunAsync(ct); + try { + for (int attempt = 0; attempt < 100 && application.Windows.Count < 2; attempt++) { + if (runTask.IsFaulted) await runTask; + await Task.Delay(100, ct); + } + + await Assert.That(application.Windows).Count().IsEqualTo(2); + await Task.WhenAll(application.Windows.Select(window => Task.Run(window.Close, ct))); + application.Shutdown(); + application.Shutdown(); + await runTask.WaitAsync(TimeSpan.FromSeconds(30), ct); + } + finally { + application.Shutdown(); + await runTask.WaitAsync(TimeSpan.FromSeconds(30), CancellationToken.None); + } + } + + [Test] + [NotInParallelInfiniTests] + [Timeout(60_000)] + public async Task ShutdownBeforeWebView2InitializationCompletesDrainsRunAsync(CancellationToken ct = default) { + if (!OperatingSystem.IsWindows() || Environment.Version.Major < 10) return; + + await using InfiniFrameApplication application = InfiniFrameApplication.Initialize() + .WithWindow("main", configure: static builder => builder.SetStartPageContent("Main")); + + Task runTask = application.RunAsync(ct); + application.Shutdown(); + application.Shutdown(); + + await runTask.WaitAsync(TimeSpan.FromSeconds(30), ct); + } + +} diff --git a/tests/InfiniTests.InfiniFrame.Application/InfiniTests.InfiniFrame.Application.csproj b/tests/InfiniTests.InfiniFrame.Application/InfiniTests.InfiniFrame.Application.csproj new file mode 100644 index 000000000..d0792227c --- /dev/null +++ b/tests/InfiniTests.InfiniFrame.Application/InfiniTests.InfiniFrame.Application.csproj @@ -0,0 +1,15 @@ + + + $(NoWarn);CS0105 + + + + + + + + + + + + diff --git a/tests/InfiniTests.InfiniFrame.Application/MultiWindowLifecycleTests.cs b/tests/InfiniTests.InfiniFrame.Application/MultiWindowLifecycleTests.cs new file mode 100644 index 000000000..97b8747f8 --- /dev/null +++ b/tests/InfiniTests.InfiniFrame.Application/MultiWindowLifecycleTests.cs @@ -0,0 +1,111 @@ +using InfiniFrame; +using InfiniFrame.Application; +using InfiniTests.TestSupport.Attributes; + +namespace InfiniTests.InfiniFrame.Application; + +[NotInParallelInfiniTests] +public sealed class MultiWindowLifecycleTests { + [Test] + [Timeout(60_000)] + public async Task FileUriWindow_LoadsAlongsidePlainWindow(CancellationToken ct = default) { + if (!OperatingSystem.IsWindows() || Environment.Version.Major < 10) return; + + string filePath = Path.Join(Path.GetTempPath(), $"{Guid.NewGuid():N}.html"); + await File.WriteAllTextAsync(filePath, "File window", ct); + + try { + await using InfiniFrameApplication application = InfiniFrameApplication.Initialize() + .WithWindow("file", builder => builder.SetStartPageUrl(new Uri(filePath).AbsoluteUri)) + .WithWindow("plain", builder => builder.SetStartPageContent("Plain")); + + Task runTask = application.RunAsync(ct); + try { + for (int attempt = 0; attempt < 100; attempt++) { + if (runTask.IsFaulted) await runTask; + + Uri? fileUri = application.TryGetWindow("file")?.Features.PageNavigation.GetCurrentUri(); + if (fileUri?.IsFile == true) break; + + await Task.Delay(100, ct); + } + + await Assert.That(application.Windows).Count().IsEqualTo(2); + await Assert.That(application.GetWindow("file").Features.PageNavigation.GetCurrentUri()?.IsFile) + .IsTrue(); + } + finally { + application.Shutdown(); + await runTask.WaitAsync(TimeSpan.FromSeconds(30), CancellationToken.None); + } + } + finally { + File.Delete(filePath); + } + } + + [Test] + [Timeout(60_000)] + public async Task ClosingOneWindow_DoesNotCloseItsSiblings(CancellationToken ct = default) { + if (!OperatingSystem.IsWindows() || Environment.Version.Major < 10) return; + + await using InfiniFrameApplication application = InfiniFrameApplication.Initialize() + .WithWindow("main", builder => builder.SetStartPageContent("Main")) + .WithWindow("settings", builder => builder.SetStartPageContent("Settings")); + + Task runTask = application.RunAsync(ct); + try { + for (int attempt = 0; attempt < 100 && application.Windows.Count < 2; attempt++) { + if (runTask.IsFaulted) await runTask; + await Task.Delay(100, ct); + } + + await Assert.That(application.Windows).Count().IsEqualTo(2); + await application.GetWindow("main").CloseAsync(); + + for (int attempt = 0; attempt < 100 && application.TryGetWindow("main") is not null; attempt++) + await Task.Delay(50, ct); + + await Assert.That(application.TryGetWindow("main")).IsNull(); + await Assert.That(application.TryGetWindow("settings")).IsNotNull(); + } + finally { + application.Shutdown(); + await runTask.WaitAsync(TimeSpan.FromSeconds(30), CancellationToken.None); + } + } + + [Test] + [Timeout(60_000)] + public async Task WindowLifecycleEvents_AreRaisedOncePerWindow(CancellationToken ct = default) { + if (!OperatingSystem.IsWindows() || Environment.Version.Major < 10) return; + + await using InfiniFrameApplication application = InfiniFrameApplication.Initialize() + .WithWindow("main", builder => builder.SetStartPageContent("Main")) + .WithWindow("secondary", builder => builder.SetStartPageContent("Secondary")); + + var lifecycleCounts = new int[2]; + application.WindowCreated += _ => Interlocked.Increment(ref lifecycleCounts[0]); + application.WindowDestroyed += _ => Interlocked.Increment(ref lifecycleCounts[1]); + + Task runTask = application.RunAsync(ct); + try { + for (int attempt = 0; attempt < 100 && Volatile.Read(ref lifecycleCounts[0]) < 2; attempt++) { + if (runTask.IsFaulted) await runTask; + await Task.Delay(100, ct); + } + + await Assert.That(lifecycleCounts[0]).IsEqualTo(2); + application.CloseAll(); + + for (int attempt = 0; attempt < 100 && Volatile.Read(ref lifecycleCounts[1]) < 2; attempt++) + await Task.Delay(100, ct); + + await Assert.That(lifecycleCounts[1]).IsEqualTo(2); + } + finally { + application.Shutdown(); + await runTask.WaitAsync(TimeSpan.FromSeconds(30), CancellationToken.None); + } + } +} diff --git a/tests/InfiniTests.InfiniFrame.Blazor/InfiniFrameJsTests.cs b/tests/InfiniTests.InfiniFrame.Blazor/InfiniFrameJsTests.cs index d1ddda09a..e631bd240 100644 --- a/tests/InfiniTests.InfiniFrame.Blazor/InfiniFrameJsTests.cs +++ b/tests/InfiniTests.InfiniFrame.Blazor/InfiniFrameJsTests.cs @@ -2,7 +2,7 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame.Blazor; -using InfiniTests.JsRuntimes; +using InfiniTests.TestSupport.JsRuntimes; using Microsoft.AspNetCore.Components; using Microsoft.Extensions.Logging; using Microsoft.JSInterop; diff --git a/tests/InfiniTests.InfiniFrame.Blazor/InfiniTests.InfiniFrame.Blazor.csproj b/tests/InfiniTests.InfiniFrame.Blazor/InfiniTests.InfiniFrame.Blazor.csproj index 290743f38..30fe77757 100644 --- a/tests/InfiniTests.InfiniFrame.Blazor/InfiniTests.InfiniFrame.Blazor.csproj +++ b/tests/InfiniTests.InfiniFrame.Blazor/InfiniTests.InfiniFrame.Blazor.csproj @@ -11,10 +11,9 @@ - - + diff --git a/tests/InfiniTests.InfiniFrame.Blazor/TestSettings.cs b/tests/InfiniTests.InfiniFrame.Blazor/TestSettings.cs index f7b615e12..1084ea750 100644 --- a/tests/InfiniTests.InfiniFrame.Blazor/TestSettings.cs +++ b/tests/InfiniTests.InfiniFrame.Blazor/TestSettings.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniTests; +using InfiniTests.TestSupport.Attributes; // --------------------------------------------------------------------------------------------------------------------- // Code diff --git a/tests/InfiniTests.InfiniFrame.BlazorWebView/FullMixedWindowConfigurationTests.cs b/tests/InfiniTests.InfiniFrame.BlazorWebView/FullMixedWindowConfigurationTests.cs new file mode 100644 index 000000000..f575292fe --- /dev/null +++ b/tests/InfiniTests.InfiniFrame.BlazorWebView/FullMixedWindowConfigurationTests.cs @@ -0,0 +1,51 @@ +using InfiniFrame; +using InfiniFrame.Application; +using InfiniFrame.BlazorWebView; +using InfiniFrame.WebServer; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; +using Microsoft.AspNetCore.Hosting; + +namespace InfiniTests.InfiniFrame.BlazorWebView; + +[NotInParallelInfiniTests] +public sealed class FullMixedWindowConfigurationTests { + [Test] + [Timeout(60_000)] + public async Task Application_CanRunPlainServerAndBlazorWindows(CancellationToken ct = default) { + if (!OperatingSystem.IsWindows() || Environment.Version.Major < 10) return; + + int port = PortUtils.GetOpenPortValue(); + await using InfiniFrameApplication application = InfiniFrameApplication.CreateBuilder() + .WithWindow("plain", builder => builder.SetStartPageContent("Plain")) + .WithWindow("server", static _ => { }) + .WithWindow("blazor", builder => builder.SetTitle("Blazor")) + .UseWebServer("server", server => server.WebHost.UseUrls($"http://127.0.0.1:{port}")) + .UseBlazorWebView("blazor", static _ => { }) + .Build(); + + Task runTask = application.RunAsync(ct); + try { + for (int attempt = 0; attempt < 100; attempt++) { + if (runTask.IsFaulted) await runTask; + + Uri? serverUri = application.TryGetWindow("server")?.Features.PageNavigation.GetCurrentUri(); + Uri? blazorUri = application.TryGetWindow("blazor")?.Features.PageNavigation.GetCurrentUri(); + if (serverUri?.Port == port && blazorUri?.Scheme == "app") break; + + await Task.Delay(100, ct); + } + + await Assert.That(application.Windows).Count().IsEqualTo(3); + await Assert.That(application.GetWindow("server").Features.PageNavigation.GetCurrentUri()?.Port) + .IsEqualTo(port); + await Assert.That(application.GetWindow("blazor").Features.PageNavigation.GetCurrentUri()?.Scheme) + .IsEqualTo("app"); + await Assert.That(application.GetWindow("plain")).IsNotNull(); + } + finally { + application.Shutdown(); + await runTask.WaitAsync(TimeSpan.FromSeconds(30), CancellationToken.None); + } + } +} diff --git a/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniFrameApplicationBlazorWebViewTests.cs b/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniFrameApplicationBlazorWebViewTests.cs new file mode 100644 index 000000000..61a271678 --- /dev/null +++ b/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniFrameApplicationBlazorWebViewTests.cs @@ -0,0 +1,76 @@ +using InfiniFrame; +using InfiniFrame.Application; +using InfiniFrame.BlazorWebView; +using InfiniTests.TestSupport.Attributes; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +namespace InfiniTests.InfiniFrame.BlazorWebView; + +[NotInParallelInfiniTests] +public sealed class InfiniFrameApplicationBlazorWebViewTests { + [Test] + public async Task UseBlazorWebView_RejectsMultipleTargetWindows() { + await Assert.That(() => InfiniFrameApplication.CreateBuilder() + .UseBlazorWebView(static _ => { }, "main", "settings")) + .Throws() + .WithMessageContaining("only one window"); + } + + [Test] + public async Task UseBlazorWebView_UsesConfiguredAppBaseUriForHttpClient() { + if (!OperatingSystem.IsWindows()) return; + + Uri customBaseUri = new("app://custom-host/"); + await using InfiniFrameApplication application = InfiniFrameApplication.CreateBuilder() + .WithWindow(static _ => { }) + .UseBlazorWebView(configuration => configuration.Configure(options => options.AppBaseUri = customBaseUri)) + .Build(); + + HttpClient client = application.RootServiceProvider.GetRequiredService(); + + await Assert.That(client.BaseAddress).IsEqualTo(customBaseUri); + await Assert.That(application.RootServiceProvider.GetRequiredService>().Value.AppBaseUri) + .IsEqualTo(customBaseUri); + } + + [Test] + public async Task WithBlazorWebView_ReturnsApplicationAndDefersWindowBuild(CancellationToken ct = default) { + if (!OperatingSystem.IsWindows()) return; + + await using InfiniFrameApplication application = InfiniFrameApplication.CreateBuilder() + .WithWindow(window => window.SetStartPageContent("Blazor")) + .UseBlazorWebView(static _ => { }) + .Build(); + + await Assert.That(application.Windows).IsEmpty(); + await Assert.That(application.RootServiceProvider.GetService()).IsNull(); + } + + [Test] + [Timeout(60_000)] + public async Task UseBlazorWebView_CombinesSingleUnnamedWindowConfiguration(CancellationToken ct = default) { + if (!OperatingSystem.IsWindows() || Environment.Version.Major < 10) return; + + await using InfiniFrameApplication application = InfiniFrameApplication.CreateBuilder() + .WithWindow(window => window.SetTitle("Combined Blazor window")) + .UseBlazorWebView(static _ => { }) + .Build(); + + Task runTask = application.RunAsync(ct); + try { + for (int attempt = 0; attempt < 100 && application.Windows.Count == 0; attempt++) { + if (runTask.IsFaulted) await runTask; + await Task.Delay(100, ct); + } + + await Assert.That(application.Windows).Count().IsEqualTo(1); + await Assert.That(application.Windows[0].Features.Decorations.Title).IsEqualTo("Combined Blazor window"); + } + finally { + application.Shutdown(); + foreach (IInfiniFrameWindow window in application.Windows) window.Close(); + await runTask.WaitAsync(TimeSpan.FromSeconds(30), CancellationToken.None); + } + } +} diff --git a/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniFrameBlazorAppBuilderTests.cs b/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniFrameBlazorAppBuilderTests.cs deleted file mode 100644 index b9ac5c782..000000000 --- a/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniFrameBlazorAppBuilderTests.cs +++ /dev/null @@ -1,428 +0,0 @@ -// --------------------------------------------------------------------------------------------------------------------- -// Imports -// --------------------------------------------------------------------------------------------------------------------- -using System.Reflection; -using InfiniFrame; -using InfiniFrame.BlazorWebView; -using InfiniFrame.NativeBridge.Parameters; -using InfiniFrame.Security; -using Microsoft.AspNetCore.Components; -using Microsoft.AspNetCore.Components.Web; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; - -namespace InfiniTests.InfiniFrame.BlazorWebView; -// --------------------------------------------------------------------------------------------------------------------- -// Code -// --------------------------------------------------------------------------------------------------------------------- -[NotInParallelInfiniTests] -[MacOsMainThreadTestExecutor] -public class InfiniFrameBlazorAppBuilderTests { - - // ----------------------------------------------------------------------------------------------------------------- - // Test Methods - // ----------------------------------------------------------------------------------------------------------------- - [Test] - public async Task Build_WithExternalProvider_ShouldUseProvidedServiceProvider(CancellationToken ct = default) { - // Arrange - var builder = InfiniFrameBlazorAppBuilder.CreateDefault(); - ServiceProvider serviceProvider = builder.Services.BuildServiceProvider(); - - // Act - InfiniFrameBlazorApp app = builder.Build(serviceProvider); - - // Assert - await Assert.That(app.ServiceProvider).IsSameReferenceAs(serviceProvider); - await app.DisposeAsync(); - } - - [Test] - public async Task Build_WithoutProvider_ShouldCreateServiceProvider(CancellationToken ct = default) { - // Arrange - var builder = InfiniFrameBlazorAppBuilder.CreateDefault(); - - // Act - InfiniFrameBlazorApp app = builder.Build(); - - // Assert - await Assert.That(app.ServiceProvider).IsNotNull(); - await app.DisposeAsync(); - } - - [Test] - public async Task CreateDefault_RootComponents_ImplementsIJsComponentConfiguration(CancellationToken ct = default) { - // Arrange - var builder = InfiniFrameBlazorAppBuilder.CreateDefault(); - - // Act - IJSComponentConfiguration configuration = builder.RootComponents; - - // Assert - await Assert.That(configuration.JSComponents).IsSameReferenceAs(builder.RootComponents.JSComponents); - } - - [Test] - public async Task CreateDefault_RootComponents_RegisterForJavaScript_WritesToSharedStore(CancellationToken ct = default) { - // Arrange - var builder = InfiniFrameBlazorAppBuilder.CreateDefault(); - - // Act - builder.RootComponents.RegisterForJavaScript("test-js-component"); - - // Assert - ServiceProvider provider = builder.Services.BuildServiceProvider(); - var store = provider.GetRequiredService(); - var config = provider.GetRequiredService(); - - await Assert.That(store).IsSameReferenceAs(builder.RootComponents.JSComponents); - await Assert.That(config.JSComponents).IsSameReferenceAs(builder.RootComponents.JSComponents); - } - - [Test] - public async Task GlobalUnhandledExceptionHandler_IsRemovedOnDispose(CancellationToken ct = default) { - // Arrange - var recordingSource = new RecordingUnhandledExceptionSource(); - var builder = InfiniFrameBlazorAppBuilder.CreateDefault(); - builder.Services.RemoveAll(); - builder.Services.AddSingleton(recordingSource); - - // Act - InfiniFrameBlazorApp app = builder.Build(); - - // Assert - await Assert.That(recordingSource.ActiveHandlers).IsEqualTo(1); - await app.DisposeAsync(); - await Assert.That(recordingSource.ActiveHandlers).IsEqualTo(0); - } - - [Test] - public async Task GlobalUnhandledExceptionHandler_RepeatedBuildDispose_DoesNotAccumulate(CancellationToken ct = default) { - // Arrange - var recordingSource = new RecordingUnhandledExceptionSource(); - var builder = InfiniFrameBlazorAppBuilder.CreateDefault(); - builder.Services.RemoveAll(); - builder.Services.AddSingleton(recordingSource); - var activeBeforeDispose = new List(); - var activeAfterDispose = new List(); - - // Act - for (int i = 0; i < 3; i++) { - InfiniFrameBlazorApp app = builder.Build(); - activeBeforeDispose.Add(recordingSource.ActiveHandlers); - await app.DisposeAsync(); - activeAfterDispose.Add(recordingSource.ActiveHandlers); - } - - // Assert - await Assert.That(activeBeforeDispose.All(static count => count == 1)).IsTrue(); - await Assert.That(activeAfterDispose.All(static count => count == 0)).IsTrue(); - await Assert.That(recordingSource.RegistrationCount).IsEqualTo(3); - } - - [Test] - public async Task GlobalUnhandledExceptionHandler_CanBeDisabled(CancellationToken ct = default) { - // Arrange - var recordingSource = new RecordingUnhandledExceptionSource(); - var builder = InfiniFrameBlazorAppBuilder.CreateDefault(); - builder.Services.RemoveAll(); - builder.Services.AddSingleton(recordingSource); - builder.Services.Configure(options => options.EnableGlobalUnhandledExceptionHandler = false); - - // Act - InfiniFrameBlazorApp app = builder.Build(); - - // Assert - await Assert.That(recordingSource.RegistrationCount).IsEqualTo(0); - await Assert.That(recordingSource.ActiveHandlers).IsEqualTo(0); - await app.DisposeAsync(); - } - - [Test] - public async Task CreateDefault_RegistersUnhandledExceptionSourceByDefault(CancellationToken ct = default) { - // Arrange - var builder = InfiniFrameBlazorAppBuilder.CreateDefault(); - ServiceProvider serviceProvider = builder.Services.BuildServiceProvider(); - - // Act - var source = serviceProvider.GetService(); - - // Assert - await Assert.That(source).IsNotNull(); - } - - [Test] - public async Task CreateDefault_ExceptionSourceRejectsNullHandler(CancellationToken ct = default) { - // Arrange - var builder = InfiniFrameBlazorAppBuilder.CreateDefault(); - ServiceProvider serviceProvider = builder.Services.BuildServiceProvider(); - var source = serviceProvider.GetRequiredService(); - - // Act - var exception = await Assert.ThrowsAsync(() => Task.Run(() => { - source.Register(null!); - })); - - // Assert - await Assert.That(exception).IsNotNull(); - await Assert.That(exception!.ParamName).IsEqualTo("handler"); - } - - [Test] - [NotInParallelInfiniTests] - public async Task SetBrowserControlInitParameters_ThroughCreateDefault_ShouldWork(CancellationToken ct = default) { - // Arrange - string[] args = []; - const string initParameters = "--force-device-scale-factor=1"; - - // Act - var appbuilder = InfiniFrameBlazorAppBuilder.CreateDefault(args, windowBuilder: builder => builder - .SetTitle("Test") - .SetBrowserControlInitParameters(initParameters) - .SetLeft(0) - .SetTop(0) - .SetSize(100, 100) - .SetResizable(false) - .SetChromeless() - .EnableSmoothScrolling(false) - ); - - // Assert - await Assert.That(appbuilder).IsNotNull(); - await Assert.That(appbuilder.WindowBuilder.Features.Browser.BrowserControlInitParameters).IsEqualTo( - initParameters - ); - } - - [Test] - [NotInParallelInfiniTests] - public async Task SetBrowserControlInitParameters_ThroughAppBuilder_ShouldWork(CancellationToken ct = default) { - // Arrange - string[] args = []; - const string initParameters = "--force-device-scale-factor=1"; - - // Act - var appbuilder = InfiniFrameBlazorAppBuilder.CreateDefault(args); - appbuilder.WindowBuilder - .SetTitle("Test") - .SetBrowserControlInitParameters(initParameters) - .SetLeft(0) - .SetTop(0) - .SetSize(100, 100) - .SetResizable(false) - .SetChromeless() - .EnableSmoothScrolling(false); - - // Assert - await Assert.That(appbuilder).IsNotNull(); - await Assert.That(appbuilder.WindowBuilder.Features.Browser.BrowserControlInitParameters).IsEqualTo( - initParameters - ); - } - - [Test] - [NotInParallelInfiniTests] - [SkipOnLinux("Given init parameters are not supported on Linux")] - public async Task SetBrowserControlInitParameters_ThroughCreateDefault_ShouldWorkOnWindow(CancellationToken ct = default) { - // Arrange - string[] args = []; - string initParameters = OperatingSystem.IsMacOS() - ? """{"developerExtrasEnabled":true}""" - : "--force-device-scale-factor=1"; - - // Act - var appbuilder = InfiniFrameBlazorAppBuilder.CreateDefault(args, windowBuilder: builder => builder - .SetTitle("Test") - .SetBrowserControlInitParameters(initParameters) - .SetLeft(0) - .SetTop(0) - .SetSize(100, 100) - .SetResizable(false) - .SetChromeless() - .EnableSmoothScrolling(false) - ); - - InfiniFrameBlazorApp app = appbuilder.Build(); - var window = app.ServiceProvider.GetRequiredService(); - - // Assert - await Assert.That(window).IsNotNull(); - await Assert.That(window.Configuration.StartupParameters.BrowserControlInitParameters).IsEqualTo( - initParameters - ); - await app.DisposeAsync(); - } - - [Test] - [NotInParallelInfiniTests] - [SkipOnLinux("Given init parameters are not supported on Linux")] - public async Task SetBrowserControlInitParameters_ThroughAppBuilder_ShouldWorkOnWindow(CancellationToken ct = default) { - // Arrange - string[] args = []; - string initParameters = OperatingSystem.IsMacOS() - ? """{"developerExtrasEnabled":true}""" - : "--force-device-scale-factor=1"; - - // Act - var appbuilder = InfiniFrameBlazorAppBuilder.CreateDefault(args); - appbuilder.WindowBuilder - .SetTitle("Test") - .SetBrowserControlInitParameters(initParameters) - .SetLeft(0) - .SetTop(0) - .SetSize(100, 100) - .SetResizable(false) - .SetChromeless() - .EnableSmoothScrolling(false); - - InfiniFrameBlazorApp app = appbuilder.Build(); - var window = app.ServiceProvider.GetRequiredService(); - - // Assert - await Assert.That(window).IsNotNull(); - await Assert.That(window.Configuration.StartupParameters.BrowserControlInitParameters).IsEqualTo( - initParameters - ); - await app.DisposeAsync(); - } - - [Test] - [NotInParallelInfiniTests] - public async Task Build_SetsStartupUrlToAppBaseForDefaultHostPage(CancellationToken ct = default) { - // Arrange - var appBuilder = InfiniFrameBlazorAppBuilder.CreateDefault(); - - // Act - InfiniFrameBlazorApp app = appBuilder.Build(); - var window = app.ServiceProvider.GetRequiredService(); - - // Assert - await Assert.That(window.Configuration.StartupParameters.StartUrl).IsEqualTo("app://localhost/"); - await app.DisposeAsync(); - } - - [Test] - [NotInParallelInfiniTests] - public async Task Build_TrustsAppOriginForFragmentNavigation(CancellationToken ct = default) { - var appBuilder = InfiniFrameBlazorAppBuilder.CreateDefault(); - - await using InfiniFrameBlazorApp app = appBuilder.Build(); - IInfiniFrameUriSecurityPolicy policy = InfiniFrameUriSecurityPolicyRegistry.GetForBuilder(appBuilder.WindowBuilder); - - await Assert.That(policy.IsTrustedOrigin(new Uri("app://localhost/index.html#settings"))).IsTrue(); - await Assert.That(policy.IsTrustedOrigin(new Uri("app://other/index.html#settings"))).IsFalse(); - } - - [Test] - [NotInParallelInfiniTests] - public async Task Build_SetsStartupUrlToConfiguredNonDefaultHostPage(CancellationToken ct = default) { - // Arrange - var appBuilder = InfiniFrameBlazorAppBuilder.CreateDefault(); - appBuilder.Services.Configure(options => { - options.HostPage = "shell/host.html"; - }); - - // Act - InfiniFrameBlazorApp app = appBuilder.Build(); - var window = app.ServiceProvider.GetRequiredService(); - - // Assert - await Assert.That(window.Configuration.StartupParameters.StartUrl).IsEqualTo("app://localhost/shell/host.html"); - await app.DisposeAsync(); - } - - [Test] - [NotInParallelInfiniTests] - public async Task Build_SetsWindowBuilderStaticAssets(CancellationToken ct = default) { - // Arrange - var appBuilder = InfiniFrameBlazorAppBuilder.CreateDefault(); - - // Act - InfiniFrameBlazorApp app = appBuilder.Build(); - - // Assert - await Assert.That(appBuilder.WindowBuilder.StaticAssets).IsNotNull(); - await Assert.That(appBuilder.WindowBuilder.StaticAssets!.BaseUri).IsEqualTo("app://localhost/"); - await Assert.That(appBuilder.WindowBuilder.StaticAssets.DefaultDocument).IsEqualTo("index.html"); - await app.DisposeAsync(); - } - - [Test] - [NotInParallelInfiniTests] - public async Task Build_PopulatesNativeStartupCustomSchemeCallback(CancellationToken ct = default) { - // Arrange - var appBuilder = InfiniFrameBlazorAppBuilder.CreateDefault(); - - // Act - InfiniFrameBlazorApp app = appBuilder.Build(); - var window = app.ServiceProvider.GetRequiredService(); - InfiniFrameNativeParameters startupParameters = window.Configuration.StartupParameters; - - FieldInfo? callbackField = typeof(InfiniFrameNativeParameters) - .GetField("CustomSchemeHandler", BindingFlags.Instance | BindingFlags.NonPublic); - object? callback = callbackField?.GetValue(startupParameters); - - // Assert - await Assert.That(callback).IsNotNull(); - await app.DisposeAsync(); - } - - [Test] - [NotInParallelInfiniTests] - public async Task Build_ExposesDebuggingThroughWindowFeatures(CancellationToken ct = default) { - // Arrange - Mock debuggingFeature = MockFactory.CreateDebuggingMock(); - Mock features = MockFactory.CreateFeaturesMock(); - Mock window = MockFactory.CreateWindowMock(); - features.Debugging.Returns(debuggingFeature.Object); - window.Features.Returns(features.Object); - window.Debugging.Returns(debuggingFeature.Object); - - var appBuilder = InfiniFrameBlazorAppBuilder.CreateDefault(); - appBuilder.Services.RemoveAll(); - appBuilder.Services.AddSingleton(window.Object); - - // Act - InfiniFrameBlazorApp app = appBuilder.Build(); - var resolvedWindow = app.ServiceProvider.GetRequiredService(); - - // Assert - await Assert.That(resolvedWindow.Debugging).IsSameReferenceAs(resolvedWindow.Features.Debugging); - await app.DisposeAsync(); - } - - private sealed class TestJsComponent : IComponent { - public void Attach(RenderHandle renderHandle) {} - - public Task SetParametersAsync(ParameterView parameters) => Task.CompletedTask; - } - - private sealed class RecordingUnhandledExceptionSource : IInfiniFrameUnhandledExceptionSource { - private int _activeHandlers; - private int _registrationCount; - - public int ActiveHandlers => Volatile.Read(ref _activeHandlers); - public int RegistrationCount => Volatile.Read(ref _registrationCount); - - public IDisposable Register(UnhandledExceptionEventHandler handler) { - _ = handler; - Interlocked.Increment(ref _registrationCount); - Interlocked.Increment(ref _activeHandlers); - return new Subscription(this); - } - - private sealed class Subscription : IDisposable { - private RecordingUnhandledExceptionSource? _owner; - - public Subscription(RecordingUnhandledExceptionSource owner) { - _owner = owner; - } - - public void Dispose() { - RecordingUnhandledExceptionSource? owner = Interlocked.Exchange(ref _owner, null); - if (owner is null) return; - - Interlocked.Decrement(ref owner._activeHandlers); - } - } - } -} diff --git a/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniFrameBlazorAppRunAsyncTests.cs b/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniFrameBlazorAppRunAsyncTests.cs deleted file mode 100644 index a65fcf48f..000000000 --- a/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniFrameBlazorAppRunAsyncTests.cs +++ /dev/null @@ -1,45 +0,0 @@ -// --------------------------------------------------------------------------------------------------------------------- -// Imports -// --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame; -using InfiniFrame.BlazorWebView; -using Microsoft.Extensions.DependencyInjection; - -namespace InfiniTests.InfiniFrame.BlazorWebView; -// --------------------------------------------------------------------------------------------------------------------- -// Code -// --------------------------------------------------------------------------------------------------------------------- -public class InfiniFrameBlazorAppRunAsyncTests { - [Test] - public async Task RunAsync_ShouldWaitAsynchronouslyAndDisposeServices(CancellationToken ct) { - // Arrange - Mock windowMock = MockFactory.CreateWindowMock(); - Mock featuresMock = MockFactory.CreateFeaturesMock(); - Mock lifecycleMock = MockFactory.CreateLifecycleMock(); - windowMock.Features.Returns(featuresMock.Object); - featuresMock.Lifecycle.Returns(lifecycleMock.Object); - lifecycleMock.WaitForCloseAsync(ct).Returns(() => ValueTask.CompletedTask); - ServiceProvider services = new ServiceCollection() - .AddSingleton(windowMock.Object) - .AddSingleton() - .BuildServiceProvider(); - var disposeProbe = services.GetRequiredService(); - var app = new InfiniFrameBlazorApp(services, new InfiniFrameRootComponentList()); - - // Act - await app.RunAsync(ct); - - // Assert - lifecycleMock.WaitForCloseAsync(ct).WasCalled(Times.Once); - lifecycleMock.WaitForClose().WasNeverCalled(); - await Assert.That(disposeProbe.IsDisposed).IsTrue(); - } - - private sealed class DisposeProbe : IDisposable { - public bool IsDisposed { get; private set; } - - public void Dispose() { - IsDisposed = true; - } - } -} diff --git a/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniFrameBlazorAppRunSyncTests.cs b/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniFrameBlazorAppRunSyncTests.cs deleted file mode 100644 index e31f8eecd..000000000 --- a/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniFrameBlazorAppRunSyncTests.cs +++ /dev/null @@ -1,44 +0,0 @@ -// --------------------------------------------------------------------------------------------------------------------- -// Imports -// --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame; -using InfiniFrame.BlazorWebView; -using Microsoft.Extensions.DependencyInjection; - -namespace InfiniTests.InfiniFrame.BlazorWebView; -// --------------------------------------------------------------------------------------------------------------------- -// Code -// --------------------------------------------------------------------------------------------------------------------- -public class InfiniFrameBlazorAppRunSyncTests { - [Test] - public async Task Run_ShouldWaitSynchronouslyAndDisposeServices() { - // Arrange - Mock windowMock = MockFactory.CreateWindowMock(); - Mock featuresMock = MockFactory.CreateFeaturesMock(); - Mock lifecycleMock = MockFactory.CreateLifecycleMock(); - windowMock.Features.Returns(featuresMock.Object); - featuresMock.Lifecycle.Returns(lifecycleMock.Object); - ServiceProvider services = new ServiceCollection() - .AddSingleton(windowMock.Object) - .AddSingleton() - .BuildServiceProvider(); - var disposeProbe = services.GetRequiredService(); - var app = new InfiniFrameBlazorApp(services, new InfiniFrameRootComponentList()); - - // Act - app.Run(); - - // Assert - lifecycleMock.WaitForClose().WasCalled(Times.Once); - lifecycleMock.WaitForCloseAsync(Any()).WasNeverCalled(); - await Assert.That(disposeProbe.IsDisposed).IsTrue(); - } - - private sealed class DisposeProbe : IDisposable { - public bool IsDisposed { get; private set; } - - public void Dispose() { - IsDisposed = true; - } - } -} diff --git a/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniFrameBlazorAppTeardownTests.cs b/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniFrameBlazorAppTeardownTests.cs deleted file mode 100644 index fd8f5b35a..000000000 --- a/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniFrameBlazorAppTeardownTests.cs +++ /dev/null @@ -1,60 +0,0 @@ -// --------------------------------------------------------------------------------------------------------------------- -// Imports -// --------------------------------------------------------------------------------------------------------------------- -using System.Runtime.Versioning; -using InfiniFrame; -using InfiniFrame.BlazorWebView; -using Microsoft.AspNetCore.Components; -using Microsoft.Extensions.DependencyInjection; - -namespace InfiniTests.InfiniFrame.BlazorWebView; -// --------------------------------------------------------------------------------------------------------------------- -// Code -// --------------------------------------------------------------------------------------------------------------------- -public sealed class InfiniFrameBlazorAppTeardownTests { - [Test] - [OnlyRunOnWindowsX64] - [NotInParallelInfiniTests] - [SupportedOSPlatform("windows")] - public async Task Run_WindowClosed_CompletesRendererDisposal(CancellationToken ct = default) { - // Arrange - var windowReady = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - var runCompleted = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - - var thread = new Thread(() => { - try { - var appBuilder = InfiniFrameBlazorAppBuilder.CreateDefault(); - appBuilder.RootComponents.Add("app"); - - InfiniFrameBlazorApp app = appBuilder.Build(); - windowReady.TrySetResult(app.ServiceProvider.GetRequiredService()); - app.Run(); - runCompleted.TrySetResult(); - } - catch (Exception exception) { - windowReady.TrySetException(exception); - runCompleted.TrySetException(exception); - } - }) { - IsBackground = true, - Name = "InfiniFrame Blazor teardown integration test" - }; - thread.SetApartmentState(ApartmentState.STA); - thread.Start(); - - // Act - IInfiniFrameWindow window = await windowReady.Task.WaitAsync(TimeSpan.FromSeconds(15), ct); - await Task.Delay(TimeSpan.FromSeconds(1), ct); - window.Close(); - - // Assert - await runCompleted.Task.WaitAsync(TimeSpan.FromSeconds(15), ct); - await Assert.That(thread.Join(TimeSpan.FromSeconds(1))).IsTrue(); - } - - private sealed class TestComponent : IComponent { - public void Attach(RenderHandle renderHandle) {} - - public Task SetParametersAsync(ParameterView parameters) => Task.CompletedTask; - } -} diff --git a/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniFrameDispatcherTests.cs b/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniFrameDispatcherTests.cs index 4358b46a1..d77d34f3b 100644 --- a/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniFrameDispatcherTests.cs +++ b/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniFrameDispatcherTests.cs @@ -3,6 +3,7 @@ // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; using InfiniFrame.BlazorWebView; +using InfiniTests.TestSupport; using Microsoft.Extensions.DependencyInjection; namespace InfiniTests.InfiniFrame.BlazorWebView; diff --git a/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniFrameHttpHandlerTests.cs b/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniFrameHttpHandlerTests.cs index c91d80026..44b0d9812 100644 --- a/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniFrameHttpHandlerTests.cs +++ b/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniFrameHttpHandlerTests.cs @@ -5,6 +5,7 @@ using System.Net; using InfiniFrame; using InfiniFrame.BlazorWebView; +using InfiniTests.TestSupport; namespace InfiniTests.InfiniFrame.BlazorWebView; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniFrameJsComponentConfigurationTests.cs b/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniFrameJsComponentConfigurationTests.cs index cc6909cd2..4516a210d 100644 --- a/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniFrameJsComponentConfigurationTests.cs +++ b/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniFrameJsComponentConfigurationTests.cs @@ -2,6 +2,7 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame.BlazorWebView; +using InfiniTests.TestSupport; using Microsoft.AspNetCore.Components.Web; using Microsoft.Extensions.Logging; diff --git a/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniFrameSynchronizationContextTests.cs b/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniFrameSynchronizationContextTests.cs index f74ed0edb..035a0c2c9 100644 --- a/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniFrameSynchronizationContextTests.cs +++ b/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniFrameSynchronizationContextTests.cs @@ -3,6 +3,7 @@ // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; using InfiniFrame.BlazorWebView; +using InfiniTests.TestSupport; using Microsoft.Extensions.DependencyInjection; namespace InfiniTests.InfiniFrame.BlazorWebView; diff --git a/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniFrameWebViewManagerTests.cs b/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniFrameWebViewManagerTests.cs index eb219093f..79d91af05 100644 --- a/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniFrameWebViewManagerTests.cs +++ b/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniFrameWebViewManagerTests.cs @@ -4,6 +4,7 @@ using System.Threading.Channels; using InfiniFrame; using InfiniFrame.BlazorWebView; +using InfiniTests.TestSupport; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Web; using Microsoft.Extensions.DependencyInjection; @@ -26,10 +27,8 @@ public class InfiniFrameWebViewManagerTests { public async Task HandleWebRequest_FragmentAndQueryAreExcludedFromLookup(CancellationToken ct = default) { byte[] expected = [.. "settings-page"u8]; var fileProvider = new RecordingFileProvider("index.html", expected); - var builder = InfiniFrameWindowBuilder.Create(); await using ServiceProvider provider = new ServiceCollection().AddLogging().BuildServiceProvider(); await using var manager = new TestableInfiniFrameWebViewManager( - builder, provider, MockFactory.CreateDispatcherMock().Object, fileProvider, @@ -58,7 +57,6 @@ public async Task HandleWebRequest_MalformedOrUntrustedUrlIsRejected(string url, var fileProvider = new RecordingFileProvider("index.html", [.. "blocked"u8]); await using ServiceProvider provider = new ServiceCollection().AddLogging().BuildServiceProvider(); await using var manager = new TestableInfiniFrameWebViewManager( - InfiniFrameWindowBuilder.Create(), provider, MockFactory.CreateDispatcherMock().Object, fileProvider, @@ -92,7 +90,6 @@ public async Task SendMessage_AfterDispose_ShouldReturnPromptly(CancellationToke Dispatcher dispatcher = MockFactory.CreateDispatcherMock().Object; var manager = new TestableInfiniFrameWebViewManager( - InfiniFrameWindowBuilder.Create(), provider, dispatcher, new NullFileProvider(), @@ -143,7 +140,6 @@ public async Task SendMessage_ShouldSerializeOutgoingMessages(CancellationToken .BuildServiceProvider(); var manager = new TestableInfiniFrameWebViewManager( - InfiniFrameWindowBuilder.Create(), provider, MockFactory.CreateDispatcherMock().Object, new NullFileProvider(), @@ -210,6 +206,43 @@ public async Task SendMessage_WhenBoundedQueueIsFull_ShouldApplyConfiguredBackpr await Assert.That(sentMessages).IsEquivalentTo(["first", "second"]); } + [Test] + public async Task SendMessage_DefaultQueueModeDoesNotSilentlyDropMessages(CancellationToken ct = default) { + var firstStarted = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var firstRelease = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var sentMessages = new List(); + Mock windowMock = MockFactory.CreateWindowMock(); + Mock featuresMock = MockFactory.CreateFeaturesMock(); + Mock webMessagingMock = MockFactory.CreateWebMessagingMock(); + windowMock.Features.Returns(featuresMock.Object); + featuresMock.WebMessaging.Returns(webMessagingMock.Object); + ValueTask returnValue = new(firstRelease.Task); + webMessagingMock.SendWebMessageAsync(Any(), Any()) + .Callback((message, _) => { + sentMessages.Add(message); + if (message == "first") firstStarted.TrySetResult(true); + }).Returns(() => returnValue); + + await using ServiceProvider provider = new ServiceCollection() + .AddLogging() + .AddSingleton(windowMock.Object) + .BuildServiceProvider(); + TestableInfiniFrameWebViewManager manager = CreateManager(provider, new InfiniFrameBlazorAppConfiguration { + WebMessageQueueCapacity = 1 + }); + + manager.SendMessageForTest("first"); + await firstStarted.Task.WaitAsync(TimeSpan.FromSeconds(5), ct); + manager.SendMessageForTest("second"); + manager.SendMessageForTest("dropped"); + firstRelease.TrySetResult(true); + + await Task.Delay(100, ct); + await manager.DisposeAsync(); + + await Assert.That(sentMessages).IsEquivalentTo(["first", "second"]); + } + [Test] public async Task DisposeAsync_ShouldCancelAndAwaitActiveMessagePumpWork(CancellationToken ct = default) { // Arrange @@ -281,7 +314,6 @@ private static TestableInfiniFrameWebViewManager CreateManager( IServiceProvider provider, InfiniFrameBlazorAppConfiguration? configuration = null ) => new( - InfiniFrameWindowBuilder.Create(), provider, MockFactory.CreateDispatcherMock().Object, new NullFileProvider(), @@ -290,14 +322,13 @@ private static TestableInfiniFrameWebViewManager CreateManager( NullLogger.Instance); private sealed class TestableInfiniFrameWebViewManager( - IInfiniFrameWindowBuilder builder, IServiceProvider provider, Dispatcher dispatcher, IFileProvider fileProvider, JSComponentConfigurationStore jsComponents, IOptions config, ILogger logger - ) : InfiniFrameWebViewManager(builder, provider, dispatcher, fileProvider, jsComponents, config, logger) { + ) : InfiniFrameWebViewManager(provider, dispatcher, fileProvider, jsComponents, config, logger) { public void SendMessageForTest(string message) => SendMessage(message); } diff --git a/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniTests.InfiniFrame.BlazorWebView.csproj b/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniTests.InfiniFrame.BlazorWebView.csproj index 3711cdd69..ea724dca6 100644 --- a/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniTests.InfiniFrame.BlazorWebView.csproj +++ b/tests/InfiniTests.InfiniFrame.BlazorWebView/InfiniTests.InfiniFrame.BlazorWebView.csproj @@ -10,10 +10,10 @@ - - + + diff --git a/tests/InfiniTests.InfiniFrame.BlazorWebView/MixedWindowConfigurationTests.cs b/tests/InfiniTests.InfiniFrame.BlazorWebView/MixedWindowConfigurationTests.cs new file mode 100644 index 000000000..a8148c377 --- /dev/null +++ b/tests/InfiniTests.InfiniFrame.BlazorWebView/MixedWindowConfigurationTests.cs @@ -0,0 +1,43 @@ +using InfiniFrame; +using InfiniFrame.Application; +using InfiniFrame.BlazorWebView; +using InfiniTests.TestSupport.Attributes; + +namespace InfiniTests.InfiniFrame.BlazorWebView; + +[NotInParallelInfiniTests] +public sealed class MixedWindowConfigurationTests { + [Test] + [Timeout(60_000)] + public async Task BlazorWebView_CanTargetOneWindowAlongsidePlainHtml(CancellationToken ct = default) { + if (!OperatingSystem.IsWindows() || Environment.Version.Major < 10) return; + + await using InfiniFrameApplication application = InfiniFrameApplication.CreateBuilder() + .WithWindow("blazor", builder => builder.SetTitle("Blazor")) + .WithWindow("plain", builder => builder.SetStartPageContent("Plain")) + .UseBlazorWebView("blazor", static _ => { }) + .Build(); + + Task runTask = application.RunAsync(ct); + try { + for (int attempt = 0; attempt < 100; attempt++) { + if (runTask.IsFaulted) await runTask; + + Uri? blazorUri = application.TryGetWindow("blazor")?.Features.PageNavigation.GetCurrentUri(); + if (blazorUri?.Scheme == "app") break; + + await Task.Delay(100, ct); + } + + await Assert.That(application.Windows).Count().IsEqualTo(2); + await Assert.That(application.GetWindow("blazor").Features.Decorations.Title).IsEqualTo("Blazor"); + await Assert.That(application.GetWindow("plain").Features.Decorations.Title).IsNotEqualTo("Blazor"); + await Assert.That(application.GetWindow("blazor").Features.PageNavigation.GetCurrentUri()?.Scheme) + .IsEqualTo("app"); + } + finally { + application.Shutdown(); + await runTask.WaitAsync(TimeSpan.FromSeconds(30), CancellationToken.None); + } + } +} diff --git a/tests/InfiniTests.InfiniFrame.BlazorWebView/TestSettings.cs b/tests/InfiniTests.InfiniFrame.BlazorWebView/TestSettings.cs index 6bec0ab5f..b91035bdb 100644 --- a/tests/InfiniTests.InfiniFrame.BlazorWebView/TestSettings.cs +++ b/tests/InfiniTests.InfiniFrame.BlazorWebView/TestSettings.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniTests; +using InfiniTests.TestSupport.Attributes; // --------------------------------------------------------------------------------------------------------------------- // Code diff --git a/tests/InfiniTests.InfiniFrame.Js/InfiniTests.InfiniFrame.Js.csproj b/tests/InfiniTests.InfiniFrame.Js/InfiniTests.InfiniFrame.Js.csproj index ada2d54c0..5111b4ce2 100644 --- a/tests/InfiniTests.InfiniFrame.Js/InfiniTests.InfiniFrame.Js.csproj +++ b/tests/InfiniTests.InfiniFrame.Js/InfiniTests.InfiniFrame.Js.csproj @@ -5,7 +5,7 @@ - + diff --git a/tests/InfiniTests.InfiniFrame.Shared/Interop/InteropEnvelopeParseResultTests.cs b/tests/InfiniTests.InfiniFrame.Js/Interop/InteropEnvelopeParseResultTests.cs similarity index 97% rename from tests/InfiniTests.InfiniFrame.Shared/Interop/InteropEnvelopeParseResultTests.cs rename to tests/InfiniTests.InfiniFrame.Js/Interop/InteropEnvelopeParseResultTests.cs index 95f9ddbc9..e6f47dd6a 100644 --- a/tests/InfiniTests.InfiniFrame.Shared/Interop/InteropEnvelopeParseResultTests.cs +++ b/tests/InfiniTests.InfiniFrame.Js/Interop/InteropEnvelopeParseResultTests.cs @@ -1,9 +1,9 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame.Interop; +using InfiniFrame.Js.Interop; -namespace InfiniTests.InfiniFrame.Shared.Interop; +namespace InfiniTests.InfiniFrame.Js.Interop; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame.Shared/Interop/InteropGetMessageErrorResponseTests.cs b/tests/InfiniTests.InfiniFrame.Js/Interop/InteropGetMessageErrorResponseTests.cs similarity index 94% rename from tests/InfiniTests.InfiniFrame.Shared/Interop/InteropGetMessageErrorResponseTests.cs rename to tests/InfiniTests.InfiniFrame.Js/Interop/InteropGetMessageErrorResponseTests.cs index 815fa816f..b661e6764 100644 --- a/tests/InfiniTests.InfiniFrame.Shared/Interop/InteropGetMessageErrorResponseTests.cs +++ b/tests/InfiniTests.InfiniFrame.Js/Interop/InteropGetMessageErrorResponseTests.cs @@ -1,9 +1,9 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame.Interop; +using InfiniFrame.Js.Interop; -namespace InfiniTests.InfiniFrame.Shared.Interop; +namespace InfiniTests.InfiniFrame.Js.Interop; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame.Shared/Interop/InteropGetMessageSuccessResponseTests.cs b/tests/InfiniTests.InfiniFrame.Js/Interop/InteropGetMessageSuccessResponseTests.cs similarity index 94% rename from tests/InfiniTests.InfiniFrame.Shared/Interop/InteropGetMessageSuccessResponseTests.cs rename to tests/InfiniTests.InfiniFrame.Js/Interop/InteropGetMessageSuccessResponseTests.cs index 996de5ba7..bd50ef1d3 100644 --- a/tests/InfiniTests.InfiniFrame.Shared/Interop/InteropGetMessageSuccessResponseTests.cs +++ b/tests/InfiniTests.InfiniFrame.Js/Interop/InteropGetMessageSuccessResponseTests.cs @@ -1,9 +1,9 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame.Interop; +using InfiniFrame.Js.Interop; -namespace InfiniTests.InfiniFrame.Shared.Interop; +namespace InfiniTests.InfiniFrame.Js.Interop; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame.Shared/Interop/JsHandlerNamesTests.cs b/tests/InfiniTests.InfiniFrame.Js/Interop/JsHandlerNamesTests.cs similarity index 97% rename from tests/InfiniTests.InfiniFrame.Shared/Interop/JsHandlerNamesTests.cs rename to tests/InfiniTests.InfiniFrame.Js/Interop/JsHandlerNamesTests.cs index a670073d9..09ecb7699 100644 --- a/tests/InfiniTests.InfiniFrame.Shared/Interop/JsHandlerNamesTests.cs +++ b/tests/InfiniTests.InfiniFrame.Js/Interop/JsHandlerNamesTests.cs @@ -1,9 +1,9 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame.Interop; +using InfiniFrame.Js.Interop; -namespace InfiniTests.InfiniFrame.Shared.Interop; +namespace InfiniTests.InfiniFrame.Js.Interop; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame.Js/TestSettings.cs b/tests/InfiniTests.InfiniFrame.Js/TestSettings.cs index f7b615e12..1084ea750 100644 --- a/tests/InfiniTests.InfiniFrame.Js/TestSettings.cs +++ b/tests/InfiniTests.InfiniFrame.Js/TestSettings.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniTests; +using InfiniTests.TestSupport.Attributes; // --------------------------------------------------------------------------------------------------------------------- // Code diff --git a/tests/InfiniTests.InfiniFrame.NativeBridge/InfiniTests.InfiniFrame.NativeBridge.csproj b/tests/InfiniTests.InfiniFrame.NativeBridge/InfiniTests.InfiniFrame.NativeBridge.csproj index 3952f9498..86c67973c 100644 --- a/tests/InfiniTests.InfiniFrame.NativeBridge/InfiniTests.InfiniFrame.NativeBridge.csproj +++ b/tests/InfiniTests.InfiniFrame.NativeBridge/InfiniTests.InfiniFrame.NativeBridge.csproj @@ -6,7 +6,7 @@ - + diff --git a/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Delegates/CustomSchemeResponseAbiTests.cs b/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Delegates/CustomSchemeResponseAbiTests.cs index 4127378f1..274f2cfaf 100644 --- a/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Delegates/CustomSchemeResponseAbiTests.cs +++ b/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Delegates/CustomSchemeResponseAbiTests.cs @@ -4,6 +4,7 @@ using System.Runtime.InteropServices; using InfiniFrame.NativeBridge; using InfiniFrame.NativeBridge.Delegates; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.NativeBridge.Managed.Delegates; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/CustomSchemeNameMemoryTests.cs b/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/CustomSchemeNameMemoryTests.cs index 6a02b45b4..fa29500f2 100644 --- a/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/CustomSchemeNameMemoryTests.cs +++ b/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/CustomSchemeNameMemoryTests.cs @@ -2,7 +2,7 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using System.Runtime.InteropServices; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; namespace InfiniTests.InfiniFrame.NativeBridge.Managed.Parameters; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeApplicationParametersEqualityComparerTests.cs b/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeApplicationParametersEqualityComparerTests.cs new file mode 100644 index 000000000..b26bc6bc3 --- /dev/null +++ b/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeApplicationParametersEqualityComparerTests.cs @@ -0,0 +1,57 @@ +// --------------------------------------------------------------------------------------------------------------------- +// Imports +// --------------------------------------------------------------------------------------------------------------------- +using InfiniFrame.NativeBridge.Parameters.Application; + +namespace InfiniTests.InfiniFrame.NativeBridge.Managed.Parameters; +// --------------------------------------------------------------------------------------------------------------------- +// Code +// --------------------------------------------------------------------------------------------------------------------- + +public sealed class InfiniFrameNativeApplicationParametersEqualityComparerTests { + private static InfiniFrameNativeApplicationParametersEqualityComparer Comparer + => InfiniFrameNativeApplicationParametersEqualityComparer.Instance; + + [Test] + public async Task Equals_SameValues_ReturnsTrue(CancellationToken ct = default) { + var first = new InfiniFrameNativeApplicationParameters { + WebView2RuntimePath = "runtime", + NotificationRegistrationId = "notifications", + AppUserModelId = "app", + DefaultNotificationIcon = "icon" + }; + InfiniFrameNativeApplicationParameters second = first; + + await Assert.That(Comparer.Equals(first, second)).IsTrue(); + } + + [Test] + [Arguments(nameof(InfiniFrameNativeApplicationParameters.WebView2RuntimePath))] + [Arguments(nameof(InfiniFrameNativeApplicationParameters.NotificationRegistrationId))] + [Arguments(nameof(InfiniFrameNativeApplicationParameters.AppUserModelId))] + [Arguments(nameof(InfiniFrameNativeApplicationParameters.DefaultNotificationIcon))] + public async Task Equals_DifferentConfigurationValue_ReturnsFalse( + string field, + CancellationToken ct = default + ) { + var first = new InfiniFrameNativeApplicationParameters(); + InfiniFrameNativeApplicationParameters second = first; + + switch (field) { + case nameof(InfiniFrameNativeApplicationParameters.WebView2RuntimePath): + second.WebView2RuntimePath = "runtime"; + break; + case nameof(InfiniFrameNativeApplicationParameters.NotificationRegistrationId): + second.NotificationRegistrationId = "notifications"; + break; + case nameof(InfiniFrameNativeApplicationParameters.AppUserModelId): + second.AppUserModelId = "app"; + break; + case nameof(InfiniFrameNativeApplicationParameters.DefaultNotificationIcon): + second.DefaultNotificationIcon = "icon"; + break; + } + + await Assert.That(Comparer.Equals(first, second)).IsFalse(); + } +} diff --git a/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeApplicationParametersMarshallerTests.cs b/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeApplicationParametersMarshallerTests.cs new file mode 100644 index 000000000..bdca0ae64 --- /dev/null +++ b/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeApplicationParametersMarshallerTests.cs @@ -0,0 +1,62 @@ +// --------------------------------------------------------------------------------------------------------------------- +// Imports +// --------------------------------------------------------------------------------------------------------------------- +using System.Runtime.InteropServices; +using InfiniFrame.NativeBridge.Parameters.Application; + +namespace InfiniTests.InfiniFrame.NativeBridge.Managed.Parameters; +// --------------------------------------------------------------------------------------------------------------------- +// Code +// --------------------------------------------------------------------------------------------------------------------- +public sealed class InfiniFrameNativeApplicationParametersMarshallerTests { + private static (int Size, bool RuntimePathPresent, bool RegistrationIdPresent, bool AppUserModelIdPresent, bool IconPresent) + MarshalParameters(InfiniFrameNativeApplicationParameters parameters) { + var marshaller = new InfiniFrameNativeApplicationParametersMarshaller.ManagedToUnmanagedIn(); + marshaller.FromManaged(parameters); + InfiniFrameNativeApplicationParametersMarshaller.Unmanaged unmanaged = marshaller.ToUnmanaged(); + (int Size, bool, bool, bool, bool) result = ( + unmanaged.Size, + unmanaged.WebView2RuntimePath != IntPtr.Zero, + unmanaged.NotificationRegistrationId != IntPtr.Zero, + unmanaged.AppUserModelId != IntPtr.Zero, + unmanaged.DefaultNotificationIcon != IntPtr.Zero + ); + marshaller.Free(); + return result; + } + + [Test] + public async Task UnmanagedLayout_MatchesManagedLayout(CancellationToken ct = default) { + await Assert.That(Marshal.SizeOf()) + .IsEqualTo(Marshal.SizeOf()); + } + + [Test] + public async Task FromManaged_CopiesAllApplicationStrings(CancellationToken ct = default) { + var parameters = new InfiniFrameNativeApplicationParameters { + WebView2RuntimePath = "runtime", + NotificationRegistrationId = "registration", + AppUserModelId = "app.id", + DefaultNotificationIcon = "icon.ico" + }; + + (int size, bool runtime, bool registration, bool appUserModel, bool icon) = MarshalParameters(parameters); + + await Assert.That(size).IsEqualTo(parameters.Size); + await Assert.That(runtime).IsTrue(); + await Assert.That(registration).IsTrue(); + await Assert.That(appUserModel).IsTrue(); + await Assert.That(icon).IsTrue(); + } + + [Test] + public async Task FromManaged_NullStringsUseNullPointers(CancellationToken ct = default) { + (_, bool runtime, bool registration, bool appUserModel, bool icon) = + MarshalParameters(new InfiniFrameNativeApplicationParameters()); + + await Assert.That(runtime).IsFalse(); + await Assert.That(registration).IsFalse(); + await Assert.That(appUserModel).IsFalse(); + await Assert.That(icon).IsFalse(); + } +} diff --git a/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeApplicationParametersTests.cs b/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeApplicationParametersTests.cs new file mode 100644 index 000000000..b4651cfff --- /dev/null +++ b/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeApplicationParametersTests.cs @@ -0,0 +1,39 @@ +using System.Runtime.InteropServices; +using InfiniFrame.NativeBridge; +using InfiniFrame.NativeBridge.Parameters.Application; + +namespace InfiniTests.InfiniFrame.NativeBridge.Managed.Parameters; + +public sealed class InfiniFrameNativeApplicationParametersTests { + [Test] + public async Task ReturnAsIsPreservesApplicationParameters(CancellationToken ct = default) { + var parameters = new InfiniFrameNativeApplicationParameters { + WebView2RuntimePath = "runtime", + NotificationRegistrationId = "registration", + AppUserModelId = "app.id", + DefaultNotificationIcon = "icon.ico" + }; + IntPtr returned = IntPtr.Zero; + + try { + InfiniFrameNativeInteropStatus status = InfiniFrameNativeTesting.NativeApplicationParametersReturnAsIsPtr( + ref parameters, + out returned + ); + + await Assert.That(status).IsEqualTo(InfiniFrameNativeInteropStatus.Success); + InfiniFrameNativeApplicationParameters actual = + Marshal.PtrToStructure(returned); + + await Assert.That(actual.WebView2RuntimePath).IsEqualTo(parameters.WebView2RuntimePath); + await Assert.That(actual.NotificationRegistrationId).IsEqualTo(parameters.NotificationRegistrationId); + await Assert.That(actual.AppUserModelId).IsEqualTo(parameters.AppUserModelId); + await Assert.That(actual.DefaultNotificationIcon).IsEqualTo(parameters.DefaultNotificationIcon); + await Assert.That(actual.Size).IsEqualTo(parameters.Size); + } + finally { + InfiniFrameNativeInteropStatus status = InfiniFrameNativeTesting.FreeApplicationParameters(returned); + await Assert.That(status).IsEqualTo(InfiniFrameNativeInteropStatus.Success); + } + } +} diff --git a/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeApplicationParametersValidatorTests.cs b/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeApplicationParametersValidatorTests.cs new file mode 100644 index 000000000..37e2d59be --- /dev/null +++ b/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeApplicationParametersValidatorTests.cs @@ -0,0 +1,22 @@ +// --------------------------------------------------------------------------------------------------------------------- +// Code +// --------------------------------------------------------------------------------------------------------------------- +using InfiniFrame.NativeBridge.Parameters.Application; + +namespace InfiniTests.InfiniFrame.NativeBridge.Managed.Parameters; +// --------------------------------------------------------------------------------------------------------------------- +// Imports +// --------------------------------------------------------------------------------------------------------------------- + +public sealed class InfiniFrameNativeApplicationParametersValidatorTests { + private static readonly InfiniFrameNativeApplicationParametersValidator Validator = new(); + + [Test] + public async Task Validate_DefaultParameters_IsValid(CancellationToken ct = default) { + var parameters = new InfiniFrameNativeApplicationParameters(); + + FluentValidation.Results.ValidationResult result = await Validator.ValidateAsync(parameters, ct); + + await Assert.That(result.IsValid).IsTrue(); + } +} diff --git a/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeParametersEqualityComparerTests.cs b/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeWindowParametersEqualityComparerTests.cs similarity index 72% rename from tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeParametersEqualityComparerTests.cs rename to tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeWindowParametersEqualityComparerTests.cs index bf9f2afe9..7e67dce03 100644 --- a/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeParametersEqualityComparerTests.cs +++ b/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeWindowParametersEqualityComparerTests.cs @@ -2,16 +2,16 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using System.Runtime.InteropServices; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; namespace InfiniTests.InfiniFrame.NativeBridge.Managed.Parameters; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- -public class InfiniFrameNativeParametersEqualityComparerTests { +public class InfiniFrameNativeWindowParametersEqualityComparerTests { - private static InfiniFrameNativeParametersEqualityComparer Comparer => InfiniFrameNativeParametersEqualityComparer.Instance; - private static InfiniFrameNativeParameters CreateDefault() => new() { + private static InfiniFrameNativeWindowParametersEqualityComparer Comparer => InfiniFrameNativeWindowParametersEqualityComparer.Instance; + private static InfiniFrameNativeWindowParameters CreateDefault() => new() { StartUrl = "https://example.com", CustomSchemeNames = new IntPtr[16] }; @@ -22,8 +22,8 @@ public class InfiniFrameNativeParametersEqualityComparerTests { [Test] public async Task Equals_TwoStructsWithSameValues_ReturnsTrue(CancellationToken ct = default) { // Arrange - InfiniFrameNativeParameters a = CreateDefault(); - InfiniFrameNativeParameters b = CreateDefault(); + InfiniFrameNativeWindowParameters a = CreateDefault(); + InfiniFrameNativeWindowParameters b = CreateDefault(); // Act bool result = Comparer.Equals(a, b); @@ -35,8 +35,8 @@ public async Task Equals_TwoStructsWithSameValues_ReturnsTrue(CancellationToken [Test] public async Task Equals_DifferentStartUrl_ReturnsFalse(CancellationToken ct = default) { // Arrange - InfiniFrameNativeParameters a = CreateDefault(); - InfiniFrameNativeParameters b = CreateDefault(); + InfiniFrameNativeWindowParameters a = CreateDefault(); + InfiniFrameNativeWindowParameters b = CreateDefault(); b.StartUrl = "https://other.com"; // Act @@ -49,8 +49,8 @@ public async Task Equals_DifferentStartUrl_ReturnsFalse(CancellationToken ct = d [Test] public async Task Equals_DifferentTitle_ReturnsFalse(CancellationToken ct = default) { // Arrange - InfiniFrameNativeParameters a = CreateDefault(); - InfiniFrameNativeParameters b = CreateDefault(); + InfiniFrameNativeWindowParameters a = CreateDefault(); + InfiniFrameNativeWindowParameters b = CreateDefault(); b.Title = "Different Title"; // Act @@ -63,8 +63,8 @@ public async Task Equals_DifferentTitle_ReturnsFalse(CancellationToken ct = defa [Test] public async Task Equals_DifferentNativeParent_ReturnsFalse(CancellationToken ct = default) { // Arrange - InfiniFrameNativeParameters a = CreateDefault(); - InfiniFrameNativeParameters b = CreateDefault(); + InfiniFrameNativeWindowParameters a = CreateDefault(); + InfiniFrameNativeWindowParameters b = CreateDefault(); b.NativeParent = new IntPtr(12345); // Act @@ -77,8 +77,8 @@ public async Task Equals_DifferentNativeParent_ReturnsFalse(CancellationToken ct [Test] public async Task Equals_DifferentLeft_ReturnsFalse(CancellationToken ct = default) { // Arrange - InfiniFrameNativeParameters a = CreateDefault(); - InfiniFrameNativeParameters b = CreateDefault(); + InfiniFrameNativeWindowParameters a = CreateDefault(); + InfiniFrameNativeWindowParameters b = CreateDefault(); b.Left = 100; // Act @@ -91,8 +91,8 @@ public async Task Equals_DifferentLeft_ReturnsFalse(CancellationToken ct = defau [Test] public async Task Equals_DifferentBooleanField_ReturnsFalse(CancellationToken ct = default) { // Arrange - InfiniFrameNativeParameters a = CreateDefault(); - InfiniFrameNativeParameters b = CreateDefault(); + InfiniFrameNativeWindowParameters a = CreateDefault(); + InfiniFrameNativeWindowParameters b = CreateDefault(); b.Resizable = true; // Act @@ -105,8 +105,8 @@ public async Task Equals_DifferentBooleanField_ReturnsFalse(CancellationToken ct [Test] public async Task Equals_DifferentRemoteDebuggingPort_ReturnsFalse(CancellationToken ct = default) { // Arrange - InfiniFrameNativeParameters a = CreateDefault(); - InfiniFrameNativeParameters b = CreateDefault(); + InfiniFrameNativeWindowParameters a = CreateDefault(); + InfiniFrameNativeWindowParameters b = CreateDefault(); b.RemoteDebuggingPort = 9222; // Act @@ -119,8 +119,8 @@ public async Task Equals_DifferentRemoteDebuggingPort_ReturnsFalse(CancellationT [Test] public async Task Equals_DifferentCustomSchemeNames_ReturnsFalse(CancellationToken ct = default) { // Arrange - InfiniFrameNativeParameters a = CreateDefault(); - InfiniFrameNativeParameters b = CreateDefault(); + InfiniFrameNativeWindowParameters a = CreateDefault(); + InfiniFrameNativeWindowParameters b = CreateDefault(); IntPtr ptr = Marshal.StringToHGlobalAnsi("app"); b.CustomSchemeNames[0] = ptr; @@ -140,7 +140,7 @@ public async Task Equals_DifferentCustomSchemeNames_ReturnsFalse(CancellationTok [Test] public async Task Equals_ObjectOverload_WithNull_ReturnsFalse(CancellationToken ct = default) { // Arrange - InfiniFrameNativeParameters a = CreateDefault(); + InfiniFrameNativeWindowParameters a = CreateDefault(); // Act bool result = Comparer.Equals(a, default); @@ -152,8 +152,8 @@ public async Task Equals_ObjectOverload_WithNull_ReturnsFalse(CancellationToken [Test] public async Task Equals_DifferentMenuBarJson_ReturnsFalse(CancellationToken ct = default) { // Arrange - InfiniFrameNativeParameters a = CreateDefault(); - InfiniFrameNativeParameters b = CreateDefault(); + InfiniFrameNativeWindowParameters a = CreateDefault(); + InfiniFrameNativeWindowParameters b = CreateDefault(); b.MenuBarJson = "{\"Items\":[]}"; // Act diff --git a/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeParametersMarshallerTests.cs b/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeWindowParametersMarshallerTests.cs similarity index 90% rename from tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeParametersMarshallerTests.cs rename to tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeWindowParametersMarshallerTests.cs index 83bc06b21..721797188 100644 --- a/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeParametersMarshallerTests.cs +++ b/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeWindowParametersMarshallerTests.cs @@ -2,21 +2,21 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using System.Runtime.InteropServices; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; namespace InfiniTests.InfiniFrame.NativeBridge.Managed.Parameters; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- /// -/// Tests for and its nested Unmanaged struct. +/// Tests for and its nested Unmanaged struct. /// /// ManagedToUnmanagedIn is a ref struct and cannot be stored in async state-machine fields, /// so all marshalling calls are encapsulated in synchronous private helpers; the async tests then assert /// on the plain values those helpers return. /// /// -public class InfiniFrameNativeParametersMarshallerTests { +public class InfiniFrameNativeWindowParametersMarshallerTests { // ----------------------------------------------------------------------------------------------------------------- // Synchronous helpers (ref-struct-safe) @@ -29,7 +29,7 @@ public class InfiniFrameNativeParametersMarshallerTests { private static (bool startUrlPtrNonNull, bool titlePtrNonNull, int left, byte centerOnInit, byte resizable) MarshalScalarFields(string? startUrl, string? title, int left, bool centerOnInit, bool resizable) { - var parameters = new InfiniFrameNativeParameters { + var parameters = new InfiniFrameNativeWindowParameters { StartUrl = startUrl, Title = title, Left = left, @@ -38,7 +38,7 @@ private static (bool startUrlPtrNonNull, bool titlePtrNonNull, int left, byte ce CustomSchemeNames = new IntPtr[16] }; - var marshaller = new InfiniFrameNativeParametersMarshaller.ManagedToUnmanagedIn(); + var marshaller = new InfiniFrameNativeWindowParametersMarshaller.ManagedToUnmanagedIn(); marshaller.FromManaged(parameters); var unmanaged = marshaller.ToUnmanaged(); @@ -55,12 +55,12 @@ private static (bool startUrlPtrNonNull, bool titlePtrNonNull, int left, byte ce /// Marshals custom-scheme-name pointers and returns whether each slot is non-null. private static bool[] MarshalCustomSchemeNames(IntPtr[] customSchemeNames) { - var parameters = new InfiniFrameNativeParameters { + var parameters = new InfiniFrameNativeWindowParameters { StartUrl = "https://example.com", CustomSchemeNames = customSchemeNames }; - var marshaller = new InfiniFrameNativeParametersMarshaller.ManagedToUnmanagedIn(); + var marshaller = new InfiniFrameNativeWindowParametersMarshaller.ManagedToUnmanagedIn(); marshaller.FromManaged(parameters); var unmanaged = marshaller.ToUnmanaged(); @@ -88,13 +88,13 @@ private static bool[] MarshalCustomSchemeNames(IntPtr[] customSchemeNames) { } private static int MarshalRemoteDebuggingPort(int remoteDebuggingPort) { - var parameters = new InfiniFrameNativeParameters { + var parameters = new InfiniFrameNativeWindowParameters { StartUrl = "https://example.com", RemoteDebuggingPort = remoteDebuggingPort, CustomSchemeNames = new IntPtr[16] }; - var marshaller = new InfiniFrameNativeParametersMarshaller.ManagedToUnmanagedIn(); + var marshaller = new InfiniFrameNativeWindowParametersMarshaller.ManagedToUnmanagedIn(); marshaller.FromManaged(parameters); var unmanaged = marshaller.ToUnmanaged(); @@ -104,13 +104,13 @@ private static int MarshalRemoteDebuggingPort(int remoteDebuggingPort) { } private static byte MarshalWebInspectorEnabled(bool webInspectorEnabled) { - var parameters = new InfiniFrameNativeParameters { + var parameters = new InfiniFrameNativeWindowParameters { StartUrl = "https://example.com", WebInspectorEnabled = webInspectorEnabled, CustomSchemeNames = new IntPtr[16] }; - var marshaller = new InfiniFrameNativeParametersMarshaller.ManagedToUnmanagedIn(); + var marshaller = new InfiniFrameNativeWindowParametersMarshaller.ManagedToUnmanagedIn(); marshaller.FromManaged(parameters); var unmanaged = marshaller.ToUnmanaged(); @@ -120,13 +120,13 @@ private static byte MarshalWebInspectorEnabled(bool webInspectorEnabled) { } private static bool MarshalDebugEventHandlerIsNonNull() { - var parameters = new InfiniFrameNativeParameters { + var parameters = new InfiniFrameNativeWindowParameters { StartUrl = "https://example.com", DebugEventHandler = (_, _, _, _, _, _, _) => {}, CustomSchemeNames = new IntPtr[16] }; - var marshaller = new InfiniFrameNativeParametersMarshaller.ManagedToUnmanagedIn(); + var marshaller = new InfiniFrameNativeWindowParametersMarshaller.ManagedToUnmanagedIn(); marshaller.FromManaged(parameters); var unmanaged = marshaller.ToUnmanaged(); @@ -141,12 +141,12 @@ private static bool MarshalDebugEventHandlerIsNonNull() { [Test] public async Task Unmanaged_SequentialLayout_SizeMatchesExpectedFieldLayout(CancellationToken ct = default) { // Arrange - // The Unmanaged struct must have the same size as InfiniFrameNativeParameters + // The Unmanaged struct must have the same size as InfiniFrameNativeWindowParameters // so that Marshal.PtrToStructure / the native boundary works correctly. - int expected = Marshal.SizeOf(); + int expected = Marshal.SizeOf(); // Act - int actual = Marshal.SizeOf(); + int actual = Marshal.SizeOf(); // Assert, verify the marshaled size matches the managed parameter struct size await Assert.That(actual).IsEqualTo(expected); diff --git a/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeParametersTests.cs b/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeWindowParametersTests.cs similarity index 92% rename from tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeParametersTests.cs rename to tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeWindowParametersTests.cs index ee455927c..434154341 100644 --- a/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeParametersTests.cs +++ b/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeWindowParametersTests.cs @@ -3,22 +3,22 @@ // --------------------------------------------------------------------------------------------------------------------- using System.Runtime.InteropServices; using InfiniFrame.NativeBridge; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; namespace InfiniTests.InfiniFrame.NativeBridge.Managed.Parameters; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- -public class InfiniFrameNativeParametersTests { +public class InfiniFrameNativeWindowParametersTests { [Test] public async Task SequentialLayout_SizeMatchesMarshalSizeOf(CancellationToken ct = default) { // Arrange // Size must be consistent across managed/native boundary; Marshal.SizeOf is the source of truth. - int expectedSize = Marshal.SizeOf(); + int expectedSize = Marshal.SizeOf(); // Act, read back to confirm it's stable - int actualSize = Marshal.SizeOf(); + int actualSize = Marshal.SizeOf(); // Assert await Assert.That(actualSize).IsEqualTo(expectedSize); @@ -43,7 +43,7 @@ public async Task NativeExport_Success_ClearsLastError(CancellationToken ct = de IntPtr newParametersPtr = IntPtr.Zero; try { - var parameters = new InfiniFrameNativeParameters { + var parameters = new InfiniFrameNativeWindowParameters { StartUrl = "https://example.org", CustomSchemeNames = customSchemeNames }; @@ -79,7 +79,7 @@ public async Task ReturnAsIsIsValid(CancellationToken ct = default) { customSchemeNames[i] = IntPtr.Zero; } - var parameters = new InfiniFrameNativeParameters { + var parameters = new InfiniFrameNativeWindowParameters { StartString = "this is a string", StartUrl = "https://www.transgenderinfo.be/", Title = "This is a title", @@ -87,9 +87,6 @@ public async Task ReturnAsIsIsValid(CancellationToken ct = default) { TemporaryFilesPath = "temp", UserAgent = "agent name", BrowserControlInitParameters = "some params", - WebView2RuntimePath = "C:\\WebView2Runtime", - NotificationRegistrationId = "some id", - WindowsAppUserModelId = "InfiniLore.InfiniFrame.Tests", RemoteDebuggingPort = 9222, NativeParent = new IntPtr(87654321), CustomSchemeNames = customSchemeNames, @@ -153,7 +150,7 @@ public async Task ReturnAsIsIsValid(CancellationToken ct = default) { await Assert.That(status).IsEqualTo(InfiniFrameNativeInteropStatus.Success); newParametersPtr = tempPtr; - var newParameters = Marshal.PtrToStructure(newParametersPtr); + var newParameters = Marshal.PtrToStructure(newParametersPtr); // Assert for (int i = 0; i < parameters.CustomSchemeNames.Length; i++) { @@ -173,9 +170,6 @@ public async Task ReturnAsIsIsValid(CancellationToken ct = default) { await Assert.That(newParameters.TemporaryFilesPath).IsEqualTo(parameters.TemporaryFilesPath); await Assert.That(newParameters.UserAgent).IsEqualTo(parameters.UserAgent); await Assert.That(newParameters.BrowserControlInitParameters).IsEqualTo(parameters.BrowserControlInitParameters); - await Assert.That(newParameters.WebView2RuntimePath).IsEqualTo(parameters.WebView2RuntimePath); - await Assert.That(newParameters.NotificationRegistrationId).IsEqualTo(parameters.NotificationRegistrationId); - await Assert.That(newParameters.WindowsAppUserModelId).IsEqualTo(parameters.WindowsAppUserModelId); await Assert.That(newParameters.RemoteDebuggingPort).IsEqualTo(parameters.RemoteDebuggingPort); await Assert.That(newParameters.NativeParent).IsEqualTo(parameters.NativeParent); await Assert.That(newParameters.Left).IsEqualTo(parameters.Left); diff --git a/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeParametersValidatorTests.cs b/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeWindowParametersValidatorTests.cs similarity index 63% rename from tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeParametersValidatorTests.cs rename to tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeWindowParametersValidatorTests.cs index a92dd648d..4f2b3cce4 100644 --- a/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeParametersValidatorTests.cs +++ b/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Parameters/InfiniFrameNativeWindowParametersValidatorTests.cs @@ -2,19 +2,19 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using FluentValidation.Results; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; namespace InfiniTests.InfiniFrame.NativeBridge.Managed.Parameters; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- -public class InfiniFrameNativeParametersValidatorTests { - private static readonly InfiniFrameNativeParametersValidator Validator = new(); +public class InfiniFrameNativeWindowParametersValidatorTests { + private static readonly InfiniFrameNativeWindowParametersValidator Validator = new(); [Test] public async Task Validate_ValidParameters_Passes(CancellationToken ct = default) { // Arrange - InfiniFrameNativeParameters parameters = CreateValidParameters(); + InfiniFrameNativeWindowParameters parameters = CreateValidParameters(); // Act ValidationResult? result = await Validator.ValidateAsync(parameters, ct); @@ -29,7 +29,7 @@ public async Task Validate_ValidParameters_Passes(CancellationToken ct = default [Arguments(int.MaxValue)] public async Task Validate_InvalidWidth_FailsValidation(int width, CancellationToken ct = default) { // Arrange - InfiniFrameNativeParameters parameters = CreateValidParameters(); + InfiniFrameNativeWindowParameters parameters = CreateValidParameters(); parameters.Width = width; // Act @@ -37,7 +37,7 @@ public async Task Validate_InvalidWidth_FailsValidation(int width, CancellationT // Assert await Assert.That(result.IsValid).IsFalse(); - await Assert.That(result.Errors.Any(error => error.PropertyName == nameof(InfiniFrameNativeParameters.Width))).IsTrue(); + await Assert.That(result.Errors.Any(error => error.PropertyName == nameof(InfiniFrameNativeWindowParameters.Width))).IsTrue(); } [Test] @@ -46,7 +46,7 @@ public async Task Validate_InvalidWidth_FailsValidation(int width, CancellationT [Arguments(int.MaxValue)] public async Task Validate_InvalidHeight_FailsValidation(int height, CancellationToken ct = default) { // Arrange - InfiniFrameNativeParameters parameters = CreateValidParameters(); + InfiniFrameNativeWindowParameters parameters = CreateValidParameters(); parameters.Height = height; // Act @@ -54,7 +54,7 @@ public async Task Validate_InvalidHeight_FailsValidation(int height, Cancellatio // Assert await Assert.That(result.IsValid).IsFalse(); - await Assert.That(result.Errors.Any(error => error.PropertyName == nameof(InfiniFrameNativeParameters.Height))).IsTrue(); + await Assert.That(result.Errors.Any(error => error.PropertyName == nameof(InfiniFrameNativeWindowParameters.Height))).IsTrue(); } [Test] @@ -64,7 +64,7 @@ public async Task Validate_InvalidHeight_FailsValidation(int height, Cancellatio [Arguments(10, int.MinValue)] public async Task Validate_InvalidPosition_FailsValidation(int left, int top, CancellationToken ct = default) { // Arrange - InfiniFrameNativeParameters parameters = CreateValidParameters(); + InfiniFrameNativeWindowParameters parameters = CreateValidParameters(); parameters.Left = left; parameters.Top = top; @@ -73,7 +73,7 @@ public async Task Validate_InvalidPosition_FailsValidation(int left, int top, Ca // Assert await Assert.That(result.IsValid).IsFalse(); - await Assert.That(result.Errors.Any(error => error.PropertyName is nameof(InfiniFrameNativeParameters.Left) or nameof(InfiniFrameNativeParameters.Top))).IsTrue(); + await Assert.That(result.Errors.Any(error => error.PropertyName is nameof(InfiniFrameNativeWindowParameters.Left) or nameof(InfiniFrameNativeWindowParameters.Top))).IsTrue(); } [Test] @@ -81,7 +81,7 @@ public async Task Validate_InvalidPosition_FailsValidation(int left, int top, Ca [Arguments(800, 600, -120, -80)] public async Task Validate_BoundaryValues_PassesWhenInRange(int width, int height, int left, int top, CancellationToken ct = default) { // Arrange - InfiniFrameNativeParameters parameters = CreateValidParameters(); + InfiniFrameNativeWindowParameters parameters = CreateValidParameters(); parameters.Width = width; parameters.Height = height; parameters.Left = left; @@ -94,35 +94,7 @@ public async Task Validate_BoundaryValues_PassesWhenInRange(int width, int heigh await Assert.That(result.IsValid).IsTrue(); } - [Test] - [Arguments("")] - [Arguments("InfiniLore Invalid")] - public async Task Validate_InvalidWindowsAppUserModelId_FailsValidation( - string value, - CancellationToken ct = default - ) { - InfiniFrameNativeParameters parameters = CreateValidParameters(); - parameters.WindowsAppUserModelId = value; - - ValidationResult result = await Validator.ValidateAsync(parameters, ct); - - await Assert.That(result.IsValid).IsFalse(); - await Assert.That(result.Errors.Any( - error => error.PropertyName == nameof(InfiniFrameNativeParameters.WindowsAppUserModelId) - )).IsTrue(); - } - - [Test] - public async Task Validate_TooLongWindowsAppUserModelId_FailsValidation(CancellationToken ct = default) { - InfiniFrameNativeParameters parameters = CreateValidParameters(); - parameters.WindowsAppUserModelId = new string('a', 129); - - ValidationResult result = await Validator.ValidateAsync(parameters, ct); - - await Assert.That(result.IsValid).IsFalse(); - } - - private static InfiniFrameNativeParameters CreateValidParameters() => + private static InfiniFrameNativeWindowParameters CreateValidParameters() => new() { StartUrl = "https://example.com", Width = 1024, diff --git a/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Windows/ColorSchemeChangeTests.cs b/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Windows/ColorSchemeChangeTests.cs index af3f7e194..5ed4c38ad 100644 --- a/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Windows/ColorSchemeChangeTests.cs +++ b/tests/InfiniTests.InfiniFrame.NativeBridge/Managed/Windows/ColorSchemeChangeTests.cs @@ -3,6 +3,7 @@ // --------------------------------------------------------------------------------------------------------------------- using System.Runtime.InteropServices; using InfiniFrame.NativeBridge; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.NativeBridge.Managed.Windows; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame.NativeBridge/TestSettings.cs b/tests/InfiniTests.InfiniFrame.NativeBridge/TestSettings.cs index f7b615e12..1084ea750 100644 --- a/tests/InfiniTests.InfiniFrame.NativeBridge/TestSettings.cs +++ b/tests/InfiniTests.InfiniFrame.NativeBridge/TestSettings.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniTests; +using InfiniTests.TestSupport.Attributes; // --------------------------------------------------------------------------------------------------------------------- // Code diff --git a/tests/InfiniTests.InfiniFrame.Shared/BlazorWebView/ManifestCandidateTests.cs b/tests/InfiniTests.InfiniFrame.Shared/BlazorWebView/ManifestCandidateTests.cs index f51bc0133..8fc734746 100644 --- a/tests/InfiniTests.InfiniFrame.Shared/BlazorWebView/ManifestCandidateTests.cs +++ b/tests/InfiniTests.InfiniFrame.Shared/BlazorWebView/ManifestCandidateTests.cs @@ -174,10 +174,10 @@ public async Task ResourceStream_CanAcceptMemoryStream(CancellationToken ct = de [Test] public async Task ResourceStream_CanAcceptFileStream(CancellationToken ct = default) { // Arrange - var tempFile = Path.GetTempFileName(); + string tempFile = Path.GetTempFileName(); try { await File.WriteAllTextAsync(tempFile, "test content"); - using var stream = File.OpenRead(tempFile); + await using FileStream stream = File.OpenRead(tempFile); // Act var candidate = new ManifestCandidate("/path", 10, stream); diff --git a/tests/InfiniTests.InfiniFrame.Shared/BlazorWebView/StaticWebAssetManifestTests.cs b/tests/InfiniTests.InfiniFrame.Shared/BlazorWebView/StaticWebAssetManifestTests.cs index 2d153313e..9a7cc8aa7 100644 --- a/tests/InfiniTests.InfiniFrame.Shared/BlazorWebView/StaticWebAssetManifestTests.cs +++ b/tests/InfiniTests.InfiniFrame.Shared/BlazorWebView/StaticWebAssetManifestTests.cs @@ -36,7 +36,7 @@ public async Task Deserialize_ValidJson_ShouldPopulateContentRoots(CancellationT string json = JsonSerializer.Serialize(manifest, StaticWebAssetsManifestJsonContext.Default.StaticWebAssetManifest); // Act - var deserialized = JsonSerializer.Deserialize(json, StaticWebAssetsManifestJsonContext.Default.StaticWebAssetManifest); + StaticWebAssetManifest? deserialized = JsonSerializer.Deserialize(json, StaticWebAssetsManifestJsonContext.Default.StaticWebAssetManifest); // Assert await Assert.That(deserialized).IsNotNull(); @@ -57,7 +57,7 @@ public async Task Deserialize_ValidJson_ShouldPopulateRoot(CancellationToken ct string json = JsonSerializer.Serialize(manifest, StaticWebAssetsManifestJsonContext.Default.StaticWebAssetManifest); // Act - var deserialized = JsonSerializer.Deserialize(json, StaticWebAssetsManifestJsonContext.Default.StaticWebAssetManifest); + StaticWebAssetManifest? deserialized = JsonSerializer.Deserialize(json, StaticWebAssetsManifestJsonContext.Default.StaticWebAssetManifest); // Assert await Assert.That(deserialized).IsNotNull(); @@ -72,7 +72,7 @@ public async Task Deserialize_EmptyObject_ShouldLeaveNullProperties(Cancellation string json = "{}"; // Act - var deserialized = JsonSerializer.Deserialize(json, StaticWebAssetsManifestJsonContext.Default.StaticWebAssetManifest); + StaticWebAssetManifest? deserialized = JsonSerializer.Deserialize(json, StaticWebAssetsManifestJsonContext.Default.StaticWebAssetManifest); // Assert await Assert.That(deserialized).IsNotNull(); @@ -86,7 +86,7 @@ public async Task Deserialize_NullContentRoots_ShouldDeserializeCorrectly(Cancel string json = "{\"ContentRoots\": null, \"Root\": null}"; // Act - var deserialized = JsonSerializer.Deserialize(json, StaticWebAssetsManifestJsonContext.Default.StaticWebAssetManifest); + StaticWebAssetManifest? deserialized = JsonSerializer.Deserialize(json, StaticWebAssetsManifestJsonContext.Default.StaticWebAssetManifest); // Assert await Assert.That(deserialized).IsNotNull(); @@ -100,7 +100,7 @@ public async Task Deserialize_PartialJson_ShouldOnlyPopulateProvidedFields(Cance string json = "{\"ContentRoots\": [\"/src\"]}"; // Act - var deserialized = JsonSerializer.Deserialize(json, StaticWebAssetsManifestJsonContext.Default.StaticWebAssetManifest); + StaticWebAssetManifest? deserialized = JsonSerializer.Deserialize(json, StaticWebAssetsManifestJsonContext.Default.StaticWebAssetManifest); // Assert await Assert.That(deserialized).IsNotNull(); @@ -136,7 +136,7 @@ public async Task RoundTrip_ComplexManifest_ShouldPreserveAllData(CancellationTo // Act string json = JsonSerializer.Serialize(manifest, StaticWebAssetsManifestJsonContext.Default.StaticWebAssetManifest); - var deserialized = JsonSerializer.Deserialize(json, StaticWebAssetsManifestJsonContext.Default.StaticWebAssetManifest); + StaticWebAssetManifest? deserialized = JsonSerializer.Deserialize(json, StaticWebAssetsManifestJsonContext.Default.StaticWebAssetManifest); // Assert await Assert.That(deserialized).IsNotNull(); @@ -156,7 +156,7 @@ public async Task RoundTrip_EmptyManifest_ShouldPreserveNulls(CancellationToken // Act string json = JsonSerializer.Serialize(manifest, StaticWebAssetsManifestJsonContext.Default.StaticWebAssetManifest); - var deserialized = JsonSerializer.Deserialize(json, StaticWebAssetsManifestJsonContext.Default.StaticWebAssetManifest); + StaticWebAssetManifest? deserialized = JsonSerializer.Deserialize(json, StaticWebAssetsManifestJsonContext.Default.StaticWebAssetManifest); // Assert await Assert.That(deserialized).IsNotNull(); diff --git a/tests/InfiniTests.InfiniFrame.Shared/BlazorWebView/StaticWebAssetTests.cs b/tests/InfiniTests.InfiniFrame.Shared/BlazorWebView/StaticWebAssetTests.cs index a78336618..d335f943a 100644 --- a/tests/InfiniTests.InfiniFrame.Shared/BlazorWebView/StaticWebAssetTests.cs +++ b/tests/InfiniTests.InfiniFrame.Shared/BlazorWebView/StaticWebAssetTests.cs @@ -67,7 +67,7 @@ public async Task RoundTrip_ShouldPreserveAllProperties(CancellationToken ct = d // Act string json = JsonSerializer.Serialize(manifest, StaticWebAssetsManifestJsonContext.Default.StaticWebAssetManifest); - var deserialized = JsonSerializer.Deserialize(json, StaticWebAssetsManifestJsonContext.Default.StaticWebAssetManifest); + StaticWebAssetManifest? deserialized = JsonSerializer.Deserialize(json, StaticWebAssetsManifestJsonContext.Default.StaticWebAssetManifest); // Assert await Assert.That(deserialized).IsNotNull(); @@ -85,7 +85,7 @@ public async Task RoundTrip_DefaultValues_ShouldPreserveDefaults(CancellationTok // Act string json = JsonSerializer.Serialize(manifest, StaticWebAssetsManifestJsonContext.Default.StaticWebAssetManifest); - var deserialized = JsonSerializer.Deserialize(json, StaticWebAssetsManifestJsonContext.Default.StaticWebAssetManifest); + StaticWebAssetManifest? deserialized = JsonSerializer.Deserialize(json, StaticWebAssetsManifestJsonContext.Default.StaticWebAssetManifest); // Assert await Assert.That(deserialized!.Root!.Asset!.ContentRootIndex).IsEqualTo(0); @@ -98,7 +98,7 @@ public async Task RoundTrip_NullSubPath_ShouldDeserializeAsEmpty(CancellationTok string json = "{\"Root\": {\"Asset\": {\"ContentRootIndex\": 1}}}"; // Act - var deserialized = JsonSerializer.Deserialize(json, StaticWebAssetsManifestJsonContext.Default.StaticWebAssetManifest); + StaticWebAssetManifest? deserialized = JsonSerializer.Deserialize(json, StaticWebAssetsManifestJsonContext.Default.StaticWebAssetManifest); // Assert await Assert.That(deserialized).IsNotNull(); diff --git a/tests/InfiniTests.InfiniFrame.Shared/Events/FileDroppedEventTests.cs b/tests/InfiniTests.InfiniFrame.Shared/Events/FileDroppedEventTests.cs index 19724c79f..45fe25818 100644 --- a/tests/InfiniTests.InfiniFrame.Shared/Events/FileDroppedEventTests.cs +++ b/tests/InfiniTests.InfiniFrame.Shared/Events/FileDroppedEventTests.cs @@ -4,6 +4,8 @@ using System.Drawing; using InfiniFrame; using InfiniFrame.DragDrop; +using InfiniFrame.Window.Events; +using InfiniTests.TestSupport; namespace InfiniTests.InfiniFrame.Shared.Events; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame.Shared/Events/HasInfiniFrameEventsStoreExtensionTests.cs b/tests/InfiniTests.InfiniFrame.Shared/Events/HasInfiniFrameEventsStoreExtensionTests.cs index fd7023f53..f17c5fae1 100644 --- a/tests/InfiniTests.InfiniFrame.Shared/Events/HasInfiniFrameEventsStoreExtensionTests.cs +++ b/tests/InfiniTests.InfiniFrame.Shared/Events/HasInfiniFrameEventsStoreExtensionTests.cs @@ -2,6 +2,7 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniFrame.Window.Events; namespace InfiniTests.InfiniFrame.Shared.Events; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame.Shared/Events/KeyedEventTests.cs b/tests/InfiniTests.InfiniFrame.Shared/Events/KeyedEventTests.cs index 22306ead7..7e9321545 100644 --- a/tests/InfiniTests.InfiniFrame.Shared/Events/KeyedEventTests.cs +++ b/tests/InfiniTests.InfiniFrame.Shared/Events/KeyedEventTests.cs @@ -2,6 +2,7 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; namespace InfiniTests.InfiniFrame.Shared.Events; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame.Shared/Events/KeyedResultEventTests.cs b/tests/InfiniTests.InfiniFrame.Shared/Events/KeyedResultEventTests.cs index fd2551daf..4cca506c0 100644 --- a/tests/InfiniTests.InfiniFrame.Shared/Events/KeyedResultEventTests.cs +++ b/tests/InfiniTests.InfiniFrame.Shared/Events/KeyedResultEventTests.cs @@ -2,6 +2,7 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; namespace InfiniTests.InfiniFrame.Shared.Events; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame.Shared/Events/OrderedEventTests.cs b/tests/InfiniTests.InfiniFrame.Shared/Events/OrderedEventTests.cs index 7038a1803..aa5138cb9 100644 --- a/tests/InfiniTests.InfiniFrame.Shared/Events/OrderedEventTests.cs +++ b/tests/InfiniTests.InfiniFrame.Shared/Events/OrderedEventTests.cs @@ -3,6 +3,7 @@ // --------------------------------------------------------------------------------------------------------------------- using System.Collections.Immutable; using InfiniFrame; +using InfiniTests.TestSupport; namespace InfiniTests.InfiniFrame.Shared.Events; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame.Shared/Events/OrderedEventWithPayloadTests.cs b/tests/InfiniTests.InfiniFrame.Shared/Events/OrderedEventWithPayloadTests.cs index ce4736ea2..d6fd90be5 100644 --- a/tests/InfiniTests.InfiniFrame.Shared/Events/OrderedEventWithPayloadTests.cs +++ b/tests/InfiniTests.InfiniFrame.Shared/Events/OrderedEventWithPayloadTests.cs @@ -3,6 +3,7 @@ // --------------------------------------------------------------------------------------------------------------------- using System.Collections.Immutable; using InfiniFrame; +using InfiniTests.TestSupport; namespace InfiniTests.InfiniFrame.Shared.Events; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame.Shared/Events/OrderedResultEventTests.cs b/tests/InfiniTests.InfiniFrame.Shared/Events/OrderedResultEventTests.cs index 29e3c718d..df79e78af 100644 --- a/tests/InfiniTests.InfiniFrame.Shared/Events/OrderedResultEventTests.cs +++ b/tests/InfiniTests.InfiniFrame.Shared/Events/OrderedResultEventTests.cs @@ -3,6 +3,7 @@ // --------------------------------------------------------------------------------------------------------------------- using System.Collections.Immutable; using InfiniFrame; +using InfiniTests.TestSupport; namespace InfiniTests.InfiniFrame.Shared.Events; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame.Shared/Features/BrowserExtensionMethodTests.cs b/tests/InfiniTests.InfiniFrame.Shared/Features/BrowserExtensionMethodTests.cs index 21aaf1291..efc41c870 100644 --- a/tests/InfiniTests.InfiniFrame.Shared/Features/BrowserExtensionMethodTests.cs +++ b/tests/InfiniTests.InfiniFrame.Shared/Features/BrowserExtensionMethodTests.cs @@ -2,6 +2,7 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; namespace InfiniTests.InfiniFrame.Shared.Features; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame.Shared/Features/DebuggingExtensionMethodTests.cs b/tests/InfiniTests.InfiniFrame.Shared/Features/DebuggingExtensionMethodTests.cs index 7921f6968..6cb40111f 100644 --- a/tests/InfiniTests.InfiniFrame.Shared/Features/DebuggingExtensionMethodTests.cs +++ b/tests/InfiniTests.InfiniFrame.Shared/Features/DebuggingExtensionMethodTests.cs @@ -2,6 +2,7 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; namespace InfiniTests.InfiniFrame.Shared.Features; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame.Shared/Features/DecorationsExtensionMethodTests.cs b/tests/InfiniTests.InfiniFrame.Shared/Features/DecorationsExtensionMethodTests.cs index 17f5d3760..13c8f52a1 100644 --- a/tests/InfiniTests.InfiniFrame.Shared/Features/DecorationsExtensionMethodTests.cs +++ b/tests/InfiniTests.InfiniFrame.Shared/Features/DecorationsExtensionMethodTests.cs @@ -2,6 +2,7 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; namespace InfiniTests.InfiniFrame.Shared.Features; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame.Shared/Features/DragDropExtensionMethodTests.cs b/tests/InfiniTests.InfiniFrame.Shared/Features/DragDropExtensionMethodTests.cs index a47f132b7..2d5a5e686 100644 --- a/tests/InfiniTests.InfiniFrame.Shared/Features/DragDropExtensionMethodTests.cs +++ b/tests/InfiniTests.InfiniFrame.Shared/Features/DragDropExtensionMethodTests.cs @@ -2,6 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniFrame.Window.Events; +using InfiniTests.TestSupport; namespace InfiniTests.InfiniFrame.Shared.Features; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame.Shared/Features/DragDropFeatureTests.cs b/tests/InfiniTests.InfiniFrame.Shared/Features/DragDropFeatureTests.cs index a6eb38f5e..fd2c95625 100644 --- a/tests/InfiniTests.InfiniFrame.Shared/Features/DragDropFeatureTests.cs +++ b/tests/InfiniTests.InfiniFrame.Shared/Features/DragDropFeatureTests.cs @@ -2,6 +2,7 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; namespace InfiniTests.InfiniFrame.Shared.Features; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame.Shared/Features/FilePickerDialogsExtensionMethodTests.cs b/tests/InfiniTests.InfiniFrame.Shared/Features/FilePickerDialogsExtensionMethodTests.cs index 93d604c49..a8b4a0836 100644 --- a/tests/InfiniTests.InfiniFrame.Shared/Features/FilePickerDialogsExtensionMethodTests.cs +++ b/tests/InfiniTests.InfiniFrame.Shared/Features/FilePickerDialogsExtensionMethodTests.cs @@ -2,6 +2,7 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; namespace InfiniTests.InfiniFrame.Shared.Features; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame.Shared/Features/InvokeExtensionMethodTests.cs b/tests/InfiniTests.InfiniFrame.Shared/Features/InvokeExtensionMethodTests.cs index 0c09d3fde..82076c6a8 100644 --- a/tests/InfiniTests.InfiniFrame.Shared/Features/InvokeExtensionMethodTests.cs +++ b/tests/InfiniTests.InfiniFrame.Shared/Features/InvokeExtensionMethodTests.cs @@ -2,6 +2,7 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; namespace InfiniTests.InfiniFrame.Shared.Features; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame.Shared/Features/LifecycleExtensionMethodTests.cs b/tests/InfiniTests.InfiniFrame.Shared/Features/LifecycleExtensionMethodTests.cs index 5a2be060e..b486290a7 100644 --- a/tests/InfiniTests.InfiniFrame.Shared/Features/LifecycleExtensionMethodTests.cs +++ b/tests/InfiniTests.InfiniFrame.Shared/Features/LifecycleExtensionMethodTests.cs @@ -2,6 +2,7 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; namespace InfiniTests.InfiniFrame.Shared.Features; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame.Shared/Features/MenuExtensionMethodTests.cs b/tests/InfiniTests.InfiniFrame.Shared/Features/MenuExtensionMethodTests.cs index 599d84603..ecd7b28b6 100644 --- a/tests/InfiniTests.InfiniFrame.Shared/Features/MenuExtensionMethodTests.cs +++ b/tests/InfiniTests.InfiniFrame.Shared/Features/MenuExtensionMethodTests.cs @@ -2,6 +2,7 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; namespace InfiniTests.InfiniFrame.Shared.Features; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame.Shared/Features/MonitorsExtensionMethodTests.cs b/tests/InfiniTests.InfiniFrame.Shared/Features/MonitorsExtensionMethodTests.cs index 1b480125a..82be49108 100644 --- a/tests/InfiniTests.InfiniFrame.Shared/Features/MonitorsExtensionMethodTests.cs +++ b/tests/InfiniTests.InfiniFrame.Shared/Features/MonitorsExtensionMethodTests.cs @@ -2,6 +2,7 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; namespace InfiniTests.InfiniFrame.Shared.Features; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame.Shared/Features/NotificationsExtensionMethodTests.cs b/tests/InfiniTests.InfiniFrame.Shared/Features/NotificationsExtensionMethodTests.cs index c73e86a78..b2ea43579 100644 --- a/tests/InfiniTests.InfiniFrame.Shared/Features/NotificationsExtensionMethodTests.cs +++ b/tests/InfiniTests.InfiniFrame.Shared/Features/NotificationsExtensionMethodTests.cs @@ -3,6 +3,7 @@ // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; using InfiniFrame.NativeBridge.Dialogs; +using InfiniTests.TestSupport; namespace InfiniTests.InfiniFrame.Shared.Features; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame.Shared/Features/PageNavigationExtensionMethodTests.cs b/tests/InfiniTests.InfiniFrame.Shared/Features/PageNavigationExtensionMethodTests.cs index 3f3258543..a14d1ed65 100644 --- a/tests/InfiniTests.InfiniFrame.Shared/Features/PageNavigationExtensionMethodTests.cs +++ b/tests/InfiniTests.InfiniFrame.Shared/Features/PageNavigationExtensionMethodTests.cs @@ -2,6 +2,7 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; namespace InfiniTests.InfiniFrame.Shared.Features; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame.Shared/Features/PositionExtensionMethodTests.cs b/tests/InfiniTests.InfiniFrame.Shared/Features/PositionExtensionMethodTests.cs index 1f0a3c3a4..c561e9bc1 100644 --- a/tests/InfiniTests.InfiniFrame.Shared/Features/PositionExtensionMethodTests.cs +++ b/tests/InfiniTests.InfiniFrame.Shared/Features/PositionExtensionMethodTests.cs @@ -2,6 +2,7 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; namespace InfiniTests.InfiniFrame.Shared.Features; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame.Shared/Features/SizeExtensionMethodTests.cs b/tests/InfiniTests.InfiniFrame.Shared/Features/SizeExtensionMethodTests.cs index 82c37d531..38f171e14 100644 --- a/tests/InfiniTests.InfiniFrame.Shared/Features/SizeExtensionMethodTests.cs +++ b/tests/InfiniTests.InfiniFrame.Shared/Features/SizeExtensionMethodTests.cs @@ -2,6 +2,7 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; namespace InfiniTests.InfiniFrame.Shared.Features; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame.Shared/Features/StateExtensionMethodTests.cs b/tests/InfiniTests.InfiniFrame.Shared/Features/StateExtensionMethodTests.cs index 071decdae..d846ebef0 100644 --- a/tests/InfiniTests.InfiniFrame.Shared/Features/StateExtensionMethodTests.cs +++ b/tests/InfiniTests.InfiniFrame.Shared/Features/StateExtensionMethodTests.cs @@ -2,6 +2,7 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; namespace InfiniTests.InfiniFrame.Shared.Features; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame.Shared/Features/TaskbarExtensionMethodTests.cs b/tests/InfiniTests.InfiniFrame.Shared/Features/TaskbarExtensionMethodTests.cs index 89a66cb71..f872b832f 100644 --- a/tests/InfiniTests.InfiniFrame.Shared/Features/TaskbarExtensionMethodTests.cs +++ b/tests/InfiniTests.InfiniFrame.Shared/Features/TaskbarExtensionMethodTests.cs @@ -2,6 +2,7 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; namespace InfiniTests.InfiniFrame.Shared.Features; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame.Shared/InfiniTests.InfiniFrame.Shared.csproj b/tests/InfiniTests.InfiniFrame.Shared/InfiniTests.InfiniFrame.Shared.csproj index 505c66437..4197e184f 100644 --- a/tests/InfiniTests.InfiniFrame.Shared/InfiniTests.InfiniFrame.Shared.csproj +++ b/tests/InfiniTests.InfiniFrame.Shared/InfiniTests.InfiniFrame.Shared.csproj @@ -9,7 +9,7 @@ - + diff --git a/tests/InfiniTests.InfiniFrame.Shared/TestSettings.cs b/tests/InfiniTests.InfiniFrame.Shared/TestSettings.cs index f7b615e12..1084ea750 100644 --- a/tests/InfiniTests.InfiniFrame.Shared/TestSettings.cs +++ b/tests/InfiniTests.InfiniFrame.Shared/TestSettings.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniTests; +using InfiniTests.TestSupport.Attributes; // --------------------------------------------------------------------------------------------------------------------- // Code diff --git a/tests/InfiniTests.InfiniFrame.Shared/Utilities/TitleStringUtilityTests.cs b/tests/InfiniTests.InfiniFrame.Shared/Utilities/TitleStringUtilityTests.cs index b7411a5ab..7576f8000 100644 --- a/tests/InfiniTests.InfiniFrame.Shared/Utilities/TitleStringUtilityTests.cs +++ b/tests/InfiniTests.InfiniFrame.Shared/Utilities/TitleStringUtilityTests.cs @@ -2,6 +2,7 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame.Utilities; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Shared.Utilities; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame.SingleFile/InfiniFramePackModeTests.cs b/tests/InfiniTests.InfiniFrame.SingleFile/InfiniFramePackModeTests.cs index c0fe96f50..47fd3e0d0 100644 --- a/tests/InfiniTests.InfiniFrame.SingleFile/InfiniFramePackModeTests.cs +++ b/tests/InfiniTests.InfiniFrame.SingleFile/InfiniFramePackModeTests.cs @@ -3,6 +3,7 @@ // --------------------------------------------------------------------------------------------------------------------- using System.Reflection; using InfiniFrame.SingleFile; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.SingleFile; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame.SingleFile/InfiniFrameSingleFileBootstrapTests.cs b/tests/InfiniTests.InfiniFrame.SingleFile/InfiniFrameSingleFileBootstrapTests.cs index 68ae99d1e..bb1c9e80e 100644 --- a/tests/InfiniTests.InfiniFrame.SingleFile/InfiniFrameSingleFileBootstrapTests.cs +++ b/tests/InfiniTests.InfiniFrame.SingleFile/InfiniFrameSingleFileBootstrapTests.cs @@ -5,6 +5,7 @@ using System.Runtime.InteropServices; using InfiniFrame.NativeBridge; using InfiniFrame.SingleFile; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.SingleFile; // --------------------------------------------------------------------------------------------------------------------- @@ -270,7 +271,7 @@ public async Task ResolveNativeLibrary_NativeDirIsEmpty_PassesNullCheck(Cancella NativeDirField.SetValue(null, ""); // Act — empty string is NOT null, so the resolver proceeds past the null check - var result = (IntPtr)resolveMethod.Invoke(null, new object?[] { "InfiniFrame.Native", typeof(InfiniFrameSingleFile).Assembly, null })!; + IntPtr result = (IntPtr)resolveMethod.Invoke(null, new object?[] { "InfiniFrame.Native", typeof(InfiniFrameSingleFile).Assembly, null })!; // Assert — result is platform-dependent; just verify no corruption await Assert.That(result).IsNotEqualTo(new IntPtr(-1)); @@ -451,7 +452,7 @@ public async Task ExtractEmbeddedNative_EmptyFileList_DoesNotThrow(CancellationT } [Test] - public async Task ExtractEmbeddedNative_NullResourceStream_SkipsFile(CancellationToken ct = default) { + public async Task ExtractEmbeddedNative_NullResourceStream_ThrowsRequiredResourceError(CancellationToken ct = default) { // Arrange ResetState(); string tempDir = Path.Join(Path.GetTempPath(), "InfiniFrame", "test_extract_" + Guid.NewGuid()); @@ -460,10 +461,9 @@ public async Task ExtractEmbeddedNative_NullResourceStream_SkipsFile(Cancellatio MethodInfo extractMethod = BootstrapType.GetMethod("ExtractEmbeddedNative", BindingFlags.NonPublic | BindingFlags.Static)!; string fakeFileName = $"nonexistent_{Guid.NewGuid()}.dll"; - // Act — should not throw; resource stream will be null and skipped - extractMethod.Invoke(null, [typeof(InfiniFrameSingleFile).Assembly, "win-x64", new[] { fakeFileName }]); - - // Assert (no exception = pass) + // Act / Assert — missing required resources fail fast. + await Assert.That(() => extractMethod.Invoke(null, [typeof(InfiniFrameSingleFile).Assembly, "win-x64", new[] { fakeFileName }])) + .Throws(); // Cleanup ResetState(); @@ -471,7 +471,7 @@ public async Task ExtractEmbeddedNative_NullResourceStream_SkipsFile(Cancellatio } [Test] - public async Task ExtractEmbeddedNative_FileAlreadyExists_SkipsFile(CancellationToken ct = default) { + public async Task ExtractEmbeddedNative_FileAlreadyExists_StillRequiresResource(CancellationToken ct = default) { // Arrange ResetState(); string tempDir = Path.Join(Path.GetTempPath(), "InfiniFrame", "test_extract_" + Guid.NewGuid()); @@ -482,12 +482,9 @@ public async Task ExtractEmbeddedNative_FileAlreadyExists_SkipsFile(Cancellation NativeDirField.SetValue(null, tempDir); MethodInfo extractMethod = BootstrapType.GetMethod("ExtractEmbeddedNative", BindingFlags.NonPublic | BindingFlags.Static)!; - // Act - extractMethod.Invoke(null, [typeof(InfiniFrameSingleFile).Assembly, "win-x64", new[] { fakeFileName }]); - - // Assert — file should still be 1 byte (not overwritten) - byte[] content = await File.ReadAllBytesAsync(filePath, ct); - await Assert.That(content.Length).IsEqualTo(1); + // Act / Assert — an existing destination cannot mask a missing embedded resource. + await Assert.That(() => extractMethod.Invoke(null, [typeof(InfiniFrameSingleFile).Assembly, "win-x64", new[] { fakeFileName }])) + .Throws(); // Cleanup ResetState(); diff --git a/tests/InfiniTests.InfiniFrame.SingleFile/InfiniFrameSingleFileTests.cs b/tests/InfiniTests.InfiniFrame.SingleFile/InfiniFrameSingleFileTests.cs index dace62c82..3eb78d8b2 100644 --- a/tests/InfiniTests.InfiniFrame.SingleFile/InfiniFrameSingleFileTests.cs +++ b/tests/InfiniTests.InfiniFrame.SingleFile/InfiniFrameSingleFileTests.cs @@ -1,178 +1,63 @@ -// --------------------------------------------------------------------------------------------------------------------- -// Imports -// --------------------------------------------------------------------------------------------------------------------- using System.Reflection; using InfiniFrame; -using InfiniFrame.BlazorWebView; using InfiniFrame.SingleFile; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.SingleFile; -// --------------------------------------------------------------------------------------------------------------------- -// Code -// --------------------------------------------------------------------------------------------------------------------- + [NotInParallelInfiniTests] public class InfiniFrameSingleFileTests { - - // ----------------------------------------------------------------------------------------------------------------- - // Initialize - // ----------------------------------------------------------------------------------------------------------------- [Test] public async Task Initialize_PackModeInactive_DoesNotThrow(CancellationToken ct = default) { - // Arrange InfiniFramePackMode.IsActive = false; - - // Act InfiniFrameSingleFile.Initialize(); - - // Assert await Assert.That(InfiniFramePackMode.IsActive).IsFalse(); - - // Cleanup InfiniFramePackMode.IsActive = false; } [Test] public async Task Initialize_PackModeInactive_DoesNotCallBootstrap(CancellationToken ct = default) { - // Arrange InfiniFramePackMode.IsActive = false; Type bootstrapType = typeof(InfiniFrameSingleFile).Assembly.GetType("InfiniFrame.InfiniFrameSingleFileBootstrap")!; FieldInfo initializedField = bootstrapType.GetField("_initialized", BindingFlags.NonPublic | BindingFlags.Static)!; int before = (int)initializedField.GetValue(null)!; - - // Act InfiniFrameSingleFile.Initialize(); - - // Assert - int after = (int)initializedField.GetValue(null)!; - await Assert.That(after).IsEqualTo(before); - - // Cleanup + await Assert.That((int)initializedField.GetValue(null)!).IsEqualTo(before); InfiniFramePackMode.IsActive = false; } [Test] public async Task Initialize_PackModeActive_DelegatesToBootstrap(CancellationToken ct = default) { - // Arrange Type bootstrapType = typeof(InfiniFrameSingleFile).Assembly.GetType("InfiniFrame.InfiniFrameSingleFileBootstrap")!; FieldInfo initializedField = bootstrapType.GetField("_initialized", BindingFlags.NonPublic | BindingFlags.Static)!; FieldInfo nativeDirField = bootstrapType.GetField("_nativeDir", BindingFlags.NonPublic | BindingFlags.Static)!; - InfiniFramePackMode.IsActive = true; initializedField.SetValue(null, 0); nativeDirField.SetValue(null, null); - - // Act InfiniFrameSingleFile.Initialize(); - - // Assert - string? nativeDir = (string?)nativeDirField.GetValue(null); - await Assert.That(nativeDir).IsNull(); - - // Cleanup + await Assert.That((string?)nativeDirField.GetValue(null)).IsNull(); InfiniFramePackMode.IsActive = false; initializedField.SetValue(null, 0); nativeDirField.SetValue(null, null); } - // ----------------------------------------------------------------------------------------------------------------- - // AddSingleFileRequirements (IInfiniFrameWindowBuilder) - // ----------------------------------------------------------------------------------------------------------------- [Test] public async Task AddSingleFileRequirements_WindowBuilder_PackModeInactive_DoesNotThrow(CancellationToken ct = default) { - // Arrange InfiniFramePackMode.IsActive = false; IInfiniFrameWindowBuilder builder = MockFactory.CreateWindowBuilderMock().Object; - - // Act builder.AddSingleFileRequirements(); - - // Assert await Assert.That(InfiniFramePackMode.IsActive).IsFalse(); - - // Cleanup InfiniFramePackMode.IsActive = false; } [Test] public async Task AddSingleFileRequirements_WindowBuilder_PackModeInactive_DoesNotModifyStaticAssets(CancellationToken ct = default) { - // Arrange InfiniFramePackMode.IsActive = false; IInfiniFrameWindowBuilder builder = MockFactory.CreateWindowBuilderMock().Object; IInfiniFrameStaticAssets? before = builder.StaticAssets; - - // Act builder.AddSingleFileRequirements(); - - // Assert await Assert.That(builder.StaticAssets).IsSameReferenceAs(before); - - // Cleanup - InfiniFramePackMode.IsActive = false; - } - - [Test] - public async Task AddSingleFileRequirements_WindowBuilder_PackModeActive_CallsExtensionMethod(CancellationToken ct = default) { - // Arrange - Type bootstrapType = typeof(InfiniFrameSingleFile).Assembly.GetType("InfiniFrame.InfiniFrameSingleFileBootstrap")!; - FieldInfo initializedField = bootstrapType.GetField("_initialized", BindingFlags.NonPublic | BindingFlags.Static)!; - - InfiniFramePackMode.IsActive = true; - initializedField.SetValue(null, 0); - - IInfiniFrameWindowBuilder builder = MockFactory.CreateWindowBuilderMock().Object; - - // Act - InvalidOperationException? ex = await Assert.ThrowsAsync(() => Task.Run(() => { - builder.AddSingleFileRequirements(); - })); - - // Assert - await Assert.That(ex!.Message).Contains("index.html"); - - // Cleanup - InfiniFramePackMode.IsActive = false; - initializedField.SetValue(null, 0); - } - - // ----------------------------------------------------------------------------------------------------------------- - // AddSingleFileRequirements (IInfiniFrameBlazorAppBuilder) - // ----------------------------------------------------------------------------------------------------------------- - [Test] - public async Task AddSingleFileRequirements_BlazorAppBuilder_PackModeInactive_DoesNotThrow(CancellationToken ct = default) { - // Arrange InfiniFramePackMode.IsActive = false; - IInfiniFrameBlazorAppBuilder builder = Mock.Of().Object; - - // Act - builder.AddSingleFileRequirements(); - - // Assert - await Assert.That(InfiniFramePackMode.IsActive).IsFalse(); - - // Cleanup - InfiniFramePackMode.IsActive = false; - } - - [Test] - public async Task AddSingleFileRequirements_BlazorAppBuilder_PackModeActive_TryCreateReturnsFalse_DoesNotRegister(CancellationToken ct = default) { - // Arrange - Type bootstrapType = typeof(InfiniFrameSingleFile).Assembly.GetType("InfiniFrame.InfiniFrameSingleFileBootstrap")!; - FieldInfo initializedField = bootstrapType.GetField("_initialized", BindingFlags.NonPublic | BindingFlags.Static)!; - - InfiniFramePackMode.IsActive = true; - initializedField.SetValue(null, 0); - - IInfiniFrameBlazorAppBuilder builder = Mock.Of().Object; - int countBefore = builder.Services.Count; - - // Act - builder.AddSingleFileRequirements(); - - // Assert - await Assert.That(builder.Services.Count).IsEqualTo(countBefore); - - // Cleanup - InfiniFramePackMode.IsActive = false; - initializedField.SetValue(null, 0); } } diff --git a/tests/InfiniTests.InfiniFrame.SingleFile/InfiniTests.InfiniFrame.SingleFile.csproj b/tests/InfiniTests.InfiniFrame.SingleFile/InfiniTests.InfiniFrame.SingleFile.csproj index cd90c2481..b5d612a33 100644 --- a/tests/InfiniTests.InfiniFrame.SingleFile/InfiniTests.InfiniFrame.SingleFile.csproj +++ b/tests/InfiniTests.InfiniFrame.SingleFile/InfiniTests.InfiniFrame.SingleFile.csproj @@ -6,7 +6,7 @@ - + diff --git a/tests/InfiniTests.InfiniFrame.WebServer/InfiniFrameApplicationWebServerTests.cs b/tests/InfiniTests.InfiniFrame.WebServer/InfiniFrameApplicationWebServerTests.cs new file mode 100644 index 000000000..00ed15d15 --- /dev/null +++ b/tests/InfiniTests.InfiniFrame.WebServer/InfiniFrameApplicationWebServerTests.cs @@ -0,0 +1,59 @@ +// --------------------------------------------------------------------------------------------------------------------- +// Imports +// --------------------------------------------------------------------------------------------------------------------- +using InfiniFrame; +using InfiniFrame.Application; +using InfiniFrame.WebServer; +using InfiniTests.TestSupport.Attributes; +using Microsoft.AspNetCore.Hosting; + +namespace InfiniTests.InfiniFrame.WebServer; +// --------------------------------------------------------------------------------------------------------------------- +// Code +// --------------------------------------------------------------------------------------------------------------------- +[NotInParallelInfiniTests] +public sealed class InfiniFrameApplicationWebServerTests { + [Test] + public async Task WithWebServer_ReturnsApplicationAndDefersWindowBuild(CancellationToken ct = default) { + if (!OperatingSystem.IsWindows()) return; + + await using InfiniFrameApplication application = InfiniFrameApplication.CreateBuilder() + .WithWindow(window => window.SetStartPageContent("Web")) + .UseWebServer(builder => builder.WebHost.UseUrls("http://127.0.0.1:0")) + .Build(); + + await Assert.That(application.Windows).IsEmpty(); + } + + [Test] + public async Task WithWebServer_RequiresExplicitIdsForMultipleWindows(CancellationToken ct = default) { + if (!OperatingSystem.IsWindows()) return; + + await Assert.That(() => InfiniFrameApplication.CreateBuilder() + .WithWindow("main", static _ => { }) + .WithWindow("settings", static _ => { }) + .UseWebServer(static _ => { }) + .Build()) + .Throws() + .WithMessageContaining("explicit window IDs"); + } + + [Test] + public async Task WithWebServer_RejectsMissingAndDuplicateIds(CancellationToken ct = default) { + if (!OperatingSystem.IsWindows()) return; + + await Assert.That(() => InfiniFrameApplication.CreateBuilder() + .WithWindow("main", static _ => { }) + .UseWebServer(static _ => { }, "missing") + .Build()) + .Throws() + .WithMessageContaining("missing"); + + await Assert.That(() => InfiniFrameApplication.CreateBuilder() + .WithWindow("main", static _ => { }) + .UseWebServer(static _ => { }, "main", "main") + .Build()) + .Throws() + .WithMessageContaining("duplicates"); + } +} diff --git a/tests/InfiniTests.InfiniFrame.WebServer/InfiniFrameWebApplicationBuilderTests.cs b/tests/InfiniTests.InfiniFrame.WebServer/InfiniFrameWebApplicationBuilderTests.cs deleted file mode 100644 index 396ecab23..000000000 --- a/tests/InfiniTests.InfiniFrame.WebServer/InfiniFrameWebApplicationBuilderTests.cs +++ /dev/null @@ -1,215 +0,0 @@ -// --------------------------------------------------------------------------------------------------------------------- -// Imports -// --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame; -using InfiniFrame.Security; -using InfiniFrame.WebServer; -using Microsoft.Extensions.DependencyInjection; - -namespace InfiniTests.InfiniFrame.WebServer; -// --------------------------------------------------------------------------------------------------------------------- -// Code -// --------------------------------------------------------------------------------------------------------------------- -public class InfiniFrameWebApplicationBuilderTests { - - [Test] - public async Task CreateBuilder_ShouldReturnBuilderWithWebAppAndWindowBuilder(CancellationToken ct = default) { - // Arrange & Act - InfiniFrameWebApplicationBuilder builder = InfiniFrameWebApplication.CreateBuilder(); - - // Assert - await Assert.That(builder).IsNotNull(); - await Assert.That(builder.WebApp).IsNotNull(); - await Assert.That(builder.WindowBuilder).IsNotNull(); - } - - [Test] - public async Task Initialize_RegistersInfiniFrameServices(CancellationToken ct = default) { - // Arrange - InfiniFrameWebApplicationBuilder builder = InfiniFrameWebApplication.CreateBuilder(); - - // Act - InfiniFrameWebApplicationBuilder result = builder.Initialize(); - - // Assert - await Assert.That(result).IsEqualTo(builder); - await Assert.That(builder.Services.Any(static d => d.ServiceType == typeof(IInfiniFrameWindowBuilder))).IsTrue(); - } - - [Test] - public async Task Initialize_RegistersIInfiniFrameWindowAsSingleton(CancellationToken ct = default) { - // Arrange - InfiniFrameWebApplicationBuilder builder = InfiniFrameWebApplication.CreateBuilder(); - - // Act - builder.Initialize(); - - // Assert - ServiceDescriptor? descriptor = builder.Services.FirstOrDefault(static d => d.ServiceType == typeof(IInfiniFrameWindow)); - await Assert.That(descriptor).IsNotNull(); - await Assert.That(descriptor!.Lifetime).IsEqualTo(ServiceLifetime.Singleton); - } - - [Test] - public async Task Initialize_RegistersGetWebMessageHandler(CancellationToken ct = default) { - // Arrange - InfiniFrameWebApplicationBuilder builder = InfiniFrameWebApplication.CreateBuilder(); - - // Act - builder.Initialize(); - - // Assert - await Assert.That(builder.WindowBuilder.EventsStore.WebMessageGetData.Count).IsGreaterThanOrEqualTo(1); - } - - [Test] - public async Task Initialize_WithUrlsConfig_SetsStartPageUrl(CancellationToken ct = default) { - // Arrange - InfiniFrameWebApplicationBuilder builder = InfiniFrameWebApplication.CreateBuilder(); - builder.WebApp.Configuration["ASPNETCORE_URLS"] = "https://localhost:7210"; - - // Act - builder.Initialize(); - - // Assert - The builder should have parsed the URL (we can verify through the security policy) - await Assert.That(builder).IsNotNull(); - } - - [Test] - public async Task Initialize_WithMultipleUrls_PicksFirstUrl(CancellationToken ct = default) { - // Arrange - InfiniFrameWebApplicationBuilder builder = InfiniFrameWebApplication.CreateBuilder(); - builder.WebApp.Configuration["ASPNETCORE_URLS"] = "https://localhost:7210;http://localhost:5210"; - - // Act - builder.Initialize(); - - // Assert - await Assert.That(builder).IsNotNull(); - } - - [Test] - public async Task Initialize_WithNoUrls_DoesNotConfigureStartPage(CancellationToken ct = default) { - // Arrange - InfiniFrameWebApplicationBuilder builder = InfiniFrameWebApplication.CreateBuilder(); - - // Act - builder.Initialize(); - - // Assert - await Assert.That(builder).IsNotNull(); - } - - [Test] - public async Task Build_CreatesWebApplication(CancellationToken ct = default) { - // Arrange - InfiniFrameWebApplicationBuilder builder = InfiniFrameWebApplication.CreateBuilder(); - - // Act - InfiniFrameWebApplication app = builder.Build(); - - // Assert - await Assert.That(app).IsNotNull(); - await Assert.That(app.WebApp).IsNotNull(); - - await app.WebApp.DisposeAsync(); - } - - [Test] - public async Task Build_WithUrlConfig_ConfiguresSecurityPolicy(CancellationToken ct = default) { - // Arrange - InfiniFrameWebApplicationBuilder builder = InfiniFrameWebApplication.CreateBuilder(); - builder.WebApp.Configuration["ASPNETCORE_URLS"] = "https://localhost:7210"; - - // Act - InfiniFrameWebApplication app = builder.Build(); - - // Assert - Security policy should have the configured origin as trusted - IInfiniFrameUriSecurityPolicy policy = InfiniFrameUriSecurityPolicyRegistry.GetForBuilder(builder.WindowBuilder); - await Assert.That(policy).IsNotNull(); - - await app.WebApp.DisposeAsync(); - } - - [Test] - public async Task Build_WithNoUrl_DoesNotConfigureSecurityPolicyOrigin(CancellationToken ct = default) { - // Arrange - InfiniFrameWebApplicationBuilder builder = InfiniFrameWebApplication.CreateBuilder(); - - // Act - InfiniFrameWebApplication app = builder.Build(); - - // Assert - IInfiniFrameUriSecurityPolicy policy = InfiniFrameUriSecurityPolicyRegistry.GetForBuilder(builder.WindowBuilder); - await Assert.That(policy).IsNotNull(); - - await app.WebApp.DisposeAsync(); - } - - [Test] - public async Task Build_ReturnsValidInfiniFrameWebApplication(CancellationToken ct = default) { - // Arrange - InfiniFrameWebApplicationBuilder builder = InfiniFrameWebApplication.CreateBuilder(); - - // Act - InfiniFrameWebApplication app = builder.Build(); - - // Assert - await Assert.That(app.Logger).IsNotNull(); - await Assert.That(app.WebApp).IsNotNull(); - - await app.WebApp.DisposeAsync(); - } - - [Test] - public async Task Services_ReturnsWebAppServices(CancellationToken ct = default) { - // Arrange - InfiniFrameWebApplicationBuilder builder = InfiniFrameWebApplication.CreateBuilder(); - - // Act - IServiceCollection services = builder.Services; - - // Assert - await Assert.That(services).IsEqualTo(builder.WebApp.Services); - } - - [Test] - public async Task Initialize_PrefersAspNetCoreUrlsOverUrlsConfig(CancellationToken ct = default) { - // Arrange - InfiniFrameWebApplicationBuilder builder = InfiniFrameWebApplication.CreateBuilder(); - builder.WebApp.Configuration["ASPNETCORE_URLS"] = "https://localhost:7210"; - builder.WebApp.Configuration["urls"] = "http://localhost:5210"; - - // Act - builder.Initialize(); - - // Assert - ASPNETCORE_URLS should take precedence - await Assert.That(builder).IsNotNull(); - } - - [Test] - public async Task Initialize_WithUrlsConfigOnly_UsesUrlsConfig(CancellationToken ct = default) { - // Arrange - InfiniFrameWebApplicationBuilder builder = InfiniFrameWebApplication.CreateBuilder(); - builder.WebApp.Configuration["urls"] = "http://localhost:5210"; - - // Act - builder.Initialize(); - - // Assert - await Assert.That(builder).IsNotNull(); - } - - [Test] - public async Task Build_Calls_ReturnsConsistentApplication(CancellationToken ct = default) { - // Arrange & Act - InfiniFrameWebApplication app1 = InfiniFrameWebApplication.CreateBuilder().Build(); - InfiniFrameWebApplication app2 = InfiniFrameWebApplication.CreateBuilder().Build(); - - // Assert - Each CreateBuilder().Build() produces a distinct instance - await Assert.That(app1).IsNotSameReferenceAs(app2); - - await app1.WebApp.DisposeAsync(); - await app2.WebApp.DisposeAsync(); - } -} diff --git a/tests/InfiniTests.InfiniFrame.WebServer/InfiniFrameWebApplicationRunAsyncTests.cs b/tests/InfiniTests.InfiniFrame.WebServer/InfiniFrameWebApplicationRunAsyncTests.cs deleted file mode 100644 index 1e9e17856..000000000 --- a/tests/InfiniTests.InfiniFrame.WebServer/InfiniFrameWebApplicationRunAsyncTests.cs +++ /dev/null @@ -1,96 +0,0 @@ -// --------------------------------------------------------------------------------------------------------------------- -// Imports -// --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame; -using InfiniFrame.WebServer; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting.Server; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging.Abstractions; - -namespace InfiniTests.InfiniFrame.WebServer; -// --------------------------------------------------------------------------------------------------------------------- -// Code -// --------------------------------------------------------------------------------------------------------------------- -public class InfiniFrameWebApplicationRunAsyncTests { - [Test] - public async Task RunAsync_ShouldStartWebAppBeforeWaitingThenStopAndDisposeIt() { - // Arrange - Mock mockWindow = MockFactory.CreateWindowMock(); - Mock features = MockFactory.CreateFeaturesMock(); - Mock lifecycle = MockFactory.CreateLifecycleMock(); - mockWindow.Features.Returns(features.Object); - features.Lifecycle.Returns(lifecycle.Object); - - WebApplicationBuilder webAppBuilder = WebApplication.CreateBuilder(); - webAppBuilder.Services.Replace(ServiceDescriptor.Singleton()); - webAppBuilder.Services.AddSingleton(); - WebApplication webApp = webAppBuilder.Build(); - var server = (NoopServer)webApp.Services.GetRequiredService(); - var appLifetime = webApp.Services.GetRequiredService(); - var disposeProbe = webApp.Services.GetRequiredService(); - bool webAppStartedBeforeWait = false; - lifecycle.WaitForCloseAsync(Any()) - .Returns(() => { - webAppStartedBeforeWait = appLifetime.ApplicationStarted.IsCancellationRequested; - return ValueTask.CompletedTask; - }); - - var app = new InfiniFrameWebApplication { - Logger = NullLogger.Instance, - WebApp = webApp, - LazyWindow = new Lazy(() => mockWindow.Object) - }; - - // Act - await app.RunAsync(); - - // Assert - lifecycle.WaitForCloseAsync(Any()).WasCalled(Times.Once); - lifecycle.WaitForClose().WasNeverCalled(); - await Assert.That(webAppStartedBeforeWait).IsTrue(); - await Assert.That(appLifetime.ApplicationStopping.IsCancellationRequested).IsTrue(); - await Assert.That(disposeProbe.IsDisposed).IsTrue(); - await Assert.That(server.StartCount).IsEqualTo(1); - await Assert.That(server.StopCount).IsEqualTo(1); - await Assert.That(server.DisposeCount).IsEqualTo(1); - } - - [Test] - public async Task RunAsync_WhenWaitFails_StillStopsAndDisposesWebApp() { - Mock mockWindow = MockFactory.CreateWindowMock(); - Mock features = MockFactory.CreateFeaturesMock(); - Mock lifecycle = MockFactory.CreateLifecycleMock(); - mockWindow.Features.Returns(features.Object); - features.Lifecycle.Returns(lifecycle.Object); - lifecycle.WaitForCloseAsync(Any()) - .Returns(() => ValueTask.FromException(new InvalidOperationException("wait failed"))); - WebApplicationBuilder builder = WebApplication.CreateBuilder(); - builder.Services.Replace(ServiceDescriptor.Singleton()); - WebApplication webApp = builder.Build(); - var server = (NoopServer)webApp.Services.GetRequiredService(); - var app = new InfiniFrameWebApplication { - Logger = NullLogger.Instance, - WebApp = webApp, - LazyWindow = new Lazy(() => mockWindow.Object) - }; - - var exception = await Assert.ThrowsAsync(() => app.RunAsync()); - - await Assert.That(exception).IsNotNull(); - await Assert.That(exception!.Message).IsEqualTo("wait failed"); - await Assert.That(server.StartCount).IsEqualTo(1); - await Assert.That(server.StopCount).IsEqualTo(1); - await Assert.That(server.DisposeCount).IsEqualTo(1); - } - - private sealed class DisposeProbe : IDisposable { - public bool IsDisposed { get; private set; } - - public void Dispose() { - IsDisposed = true; - } - } -} diff --git a/tests/InfiniTests.InfiniFrame.WebServer/InfiniFrameWebApplicationRunSyncTests.cs b/tests/InfiniTests.InfiniFrame.WebServer/InfiniFrameWebApplicationRunSyncTests.cs deleted file mode 100644 index 59a904bd3..000000000 --- a/tests/InfiniTests.InfiniFrame.WebServer/InfiniFrameWebApplicationRunSyncTests.cs +++ /dev/null @@ -1,91 +0,0 @@ -// --------------------------------------------------------------------------------------------------------------------- -// Imports -// --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame; -using InfiniFrame.WebServer; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting.Server; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging.Abstractions; - -namespace InfiniTests.InfiniFrame.WebServer; -// --------------------------------------------------------------------------------------------------------------------- -// Code -// --------------------------------------------------------------------------------------------------------------------- -public class InfiniFrameWebApplicationRunSyncTests { - [Test] - public async Task Run_ShouldStartWebAppBeforeWaitingThenStopAndDisposeIt() { - // Arrange - Mock mockWindow = MockFactory.CreateWindowMock(); - Mock features = MockFactory.CreateFeaturesMock(); - Mock lifecycle = MockFactory.CreateLifecycleMock(); - mockWindow.Features.Returns(features.Object); - features.Lifecycle.Returns(lifecycle.Object); - - WebApplicationBuilder webAppBuilder = WebApplication.CreateBuilder(); - webAppBuilder.Services.Replace(ServiceDescriptor.Singleton()); - webAppBuilder.Services.AddSingleton(); - WebApplication webApp = webAppBuilder.Build(); - var server = (NoopServer)webApp.Services.GetRequiredService(); - var appLifetime = webApp.Services.GetRequiredService(); - var disposeProbe = webApp.Services.GetRequiredService(); - bool webAppStartedBeforeWait = false; - lifecycle.WaitForClose().Callback(() => webAppStartedBeforeWait = appLifetime.ApplicationStarted.IsCancellationRequested); - - var app = new InfiniFrameWebApplication { - Logger = NullLogger.Instance, - WebApp = webApp, - LazyWindow = new Lazy(() => mockWindow.Object) - }; - - // Act - app.Run(); - - // Assert - lifecycle.WaitForClose().WasCalled(Times.Once); - lifecycle.WaitForCloseAsync(Any()).WasNeverCalled(); - await Assert.That(webAppStartedBeforeWait).IsTrue(); - await Assert.That(appLifetime.ApplicationStopping.IsCancellationRequested).IsTrue(); - await Assert.That(disposeProbe.IsDisposed).IsTrue(); - await Assert.That(server.StartCount).IsEqualTo(1); - await Assert.That(server.StopCount).IsEqualTo(1); - await Assert.That(server.DisposeCount).IsEqualTo(1); - } - - [Test] - public async Task Run_WhenWaitFails_StillStopsAndDisposesWebApp() { - Mock mockWindow = MockFactory.CreateWindowMock(); - Mock features = MockFactory.CreateFeaturesMock(); - Mock lifecycle = MockFactory.CreateLifecycleMock(); - mockWindow.Features.Returns(features.Object); - features.Lifecycle.Returns(lifecycle.Object); - lifecycle.WaitForClose().Callback(() => throw new InvalidOperationException("wait failed")); - WebApplicationBuilder builder = WebApplication.CreateBuilder(); - builder.Services.Replace(ServiceDescriptor.Singleton()); - WebApplication webApp = builder.Build(); - var server = (NoopServer)webApp.Services.GetRequiredService(); - var app = new InfiniFrameWebApplication { - Logger = NullLogger.Instance, - WebApp = webApp, - LazyWindow = new Lazy(() => mockWindow.Object) - }; - - var exception = Assert.Throws(() => app.Run()); - - await Assert.That(exception).IsNotNull(); - await Assert.That(exception.Message).IsEqualTo("wait failed"); - await Assert.That(server.StartCount).IsEqualTo(1); - await Assert.That(server.StopCount).IsEqualTo(1); - await Assert.That(server.DisposeCount).IsEqualTo(1); - } - - private sealed class DisposeProbe : IDisposable { - public bool IsDisposed { get; private set; } - - public void Dispose() { - IsDisposed = true; - } - } -} diff --git a/tests/InfiniTests.InfiniFrame.WebServer/InfiniFrameWebApplicationStopAsyncTests.cs b/tests/InfiniTests.InfiniFrame.WebServer/InfiniFrameWebApplicationStopAsyncTests.cs deleted file mode 100644 index 0a5e7e23e..000000000 --- a/tests/InfiniTests.InfiniFrame.WebServer/InfiniFrameWebApplicationStopAsyncTests.cs +++ /dev/null @@ -1,42 +0,0 @@ -// --------------------------------------------------------------------------------------------------------------------- -// Imports -// --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame; -using InfiniFrame.WebServer; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging.Abstractions; - -namespace InfiniTests.InfiniFrame.WebServer; -// --------------------------------------------------------------------------------------------------------------------- -// Code -// --------------------------------------------------------------------------------------------------------------------- -public class InfiniFrameWebApplicationStopAsyncTests { - [Test] - public async Task StopAsync_ShouldStopWebAppAndCloseWindow(CancellationToken ct) { - // Arrange - Mock mockWindow = MockFactory.CreateWindowMock(); - Mock features = MockFactory.CreateFeaturesMock(); - Mock lifecycle = MockFactory.CreateLifecycleMock(); - mockWindow.Features.Returns(features.Object); - features.Lifecycle.Returns(lifecycle.Object); - WebApplication webApp = WebApplication.CreateBuilder().Build(); - var appLifetime = webApp.Services.GetRequiredService(); - var app = new InfiniFrameWebApplication { - Logger = NullLogger.Instance, - WebApp = webApp, - LazyWindow = new Lazy(() => mockWindow.Object) - }; - - // Act - await app.StopAsync(ct); - - // Assert - lifecycle.CloseAsync(ct).WasCalled(Times.Once); - lifecycle.WaitForCloseAsync(ct).WasCalled(Times.Once); - await Assert.That(appLifetime.ApplicationStopping.IsCancellationRequested).IsTrue(); - - await webApp.DisposeAsync(); - } -} diff --git a/tests/InfiniTests.InfiniFrame.WebServer/InfiniFrameWebApplicationStopSyncTests.cs b/tests/InfiniTests.InfiniFrame.WebServer/InfiniFrameWebApplicationStopSyncTests.cs deleted file mode 100644 index f3c4906ef..000000000 --- a/tests/InfiniTests.InfiniFrame.WebServer/InfiniFrameWebApplicationStopSyncTests.cs +++ /dev/null @@ -1,42 +0,0 @@ -// --------------------------------------------------------------------------------------------------------------------- -// Imports -// --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame; -using InfiniFrame.WebServer; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging.Abstractions; - -namespace InfiniTests.InfiniFrame.WebServer; -// --------------------------------------------------------------------------------------------------------------------- -// Code -// --------------------------------------------------------------------------------------------------------------------- -public class InfiniFrameWebApplicationStopSyncTests { - [Test] - public async Task Stop_ShouldStopWebAppAndCloseWindow() { - // Arrange - Mock mockWindow = MockFactory.CreateWindowMock(); - Mock features = MockFactory.CreateFeaturesMock(); - Mock lifecycle = MockFactory.CreateLifecycleMock(); - mockWindow.Features.Returns(features.Object); - features.Lifecycle.Returns(lifecycle.Object); - WebApplication webApp = WebApplication.CreateBuilder().Build(); - var appLifetime = webApp.Services.GetRequiredService(); - var app = new InfiniFrameWebApplication { - Logger = NullLogger.Instance, - WebApp = webApp, - LazyWindow = new Lazy(() => mockWindow.Object) - }; - - // Act - app.Stop(); - - // Assert - lifecycle.CloseAsync(Any()).WasCalled(Times.Once); - lifecycle.WaitForCloseAsync(Any()).WasCalled(Times.Once); - await Assert.That(appLifetime.ApplicationStopping.IsCancellationRequested).IsTrue(); - - await webApp.DisposeAsync(); - } -} diff --git a/tests/InfiniTests.InfiniFrame.WebServer/InfiniFrameWebApplicationTests.cs b/tests/InfiniTests.InfiniFrame.WebServer/InfiniFrameWebApplicationTests.cs deleted file mode 100644 index 66457b387..000000000 --- a/tests/InfiniTests.InfiniFrame.WebServer/InfiniFrameWebApplicationTests.cs +++ /dev/null @@ -1,202 +0,0 @@ -// --------------------------------------------------------------------------------------------------------------------- -// Imports -// --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame; -using InfiniFrame.Blazor; -using InfiniFrame.WebServer; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging.Abstractions; - -namespace InfiniTests.InfiniFrame.WebServer; -// --------------------------------------------------------------------------------------------------------------------- -// Code -// --------------------------------------------------------------------------------------------------------------------- -public class InfiniFrameWebApplicationTests { - private const int DefaultGetMessageHandlerCount = 1; - - private static (Mock Mock, IInfiniFrameWindow Object) CreateMockWindow() { - Mock mockWindow = MockFactory.CreateWindowMock(); - var eventsStore = new InfiniFrameEventsStore(); - mockWindow.Events.Returns(new InfiniFrameEvents(eventsStore, NullLogger.Instance)); - mockWindow.EventsStore.Returns(eventsStore); - return (mockWindow, mockWindow.Object); - } - - [Test] - public async Task CreateBuilder_ShouldReturnValidBuilder() { - // Arrange & Act - InfiniFrameWebApplicationBuilder builder = InfiniFrameWebApplication.CreateBuilder(); - - // Assert - await Assert.That(builder).IsNotNull(); - await Assert.That(builder.WebApp).IsNotNull(); - await Assert.That(builder.WindowBuilder).IsNotNull(); - } - - [Test] - public async Task Build_DefaultWebMessageHandlersWithoutBlazorJsRuntime_ShouldPassServiceValidation() { - // Arrange - InfiniFrameWebApplicationBuilder builder = InfiniFrameWebApplication.CreateBuilder(); - builder.WebApp.Host.UseDefaultServiceProvider(static options => { - options.ValidateOnBuild = true; - options.ValidateScopes = true; - }); - - // Act - InfiniFrameWebApplication app = builder.Build(); - - // Assert - await Assert.That(builder.Services.Any(static descriptor => descriptor.ServiceType == typeof(IInfiniFrameJs))) - .IsFalse(); - await Assert.That(builder.WindowBuilder.EventsStore.WebMessageGetData.Count).IsGreaterThanOrEqualTo(DefaultGetMessageHandlerCount); - - await app.WebApp.DisposeAsync(); - } - - [Test] - public async Task UseAutoServerClose_WhenWindowNotCreated_ShouldRegisterWithBuilder() { - // Arrange - Mock mockWindowBuilder = MockFactory.CreateWindowBuilderMock(); - var mockBuilderEventsStore = new InfiniFrameEventsStore(); - mockWindowBuilder.EventsStore.Returns(mockBuilderEventsStore); - - WebApplicationBuilder webAppBuilder = WebApplication.CreateBuilder(); - webAppBuilder.Services.AddSingleton(mockWindowBuilder.Object); - - WebApplication webApp = webAppBuilder.Build(); - var lazyWindow = new Lazy(() => CreateMockWindow().Object); - - var app = new InfiniFrameWebApplication { - Logger = NullLogger.Instance, - WebApp = webApp, - LazyWindow = lazyWindow - }; - - // Act - InfiniFrameWebApplication result = app.UseAutoServerClose(); - - // Assert - await Assert.That(result).IsEqualTo(app); - await Assert.That(mockBuilderEventsStore.Closing.Snapshot.Length).IsGreaterThan(0); - } - - [Test] - public async Task UseAutoServerClose_WhenWindowCreated_ShouldRegisterWithWindow() { - // Arrange - (_, IInfiniFrameWindow mockWindow) = CreateMockWindow(); - IInfiniFrameEvents mockEvents = mockWindow.Events; - - WebApplicationBuilder webAppBuilder = WebApplication.CreateBuilder(); - WebApplication webApp = webAppBuilder.Build(); - - var lazyWindow = new Lazy(() => mockWindow); - // Force window creation - _ = lazyWindow.Value; - - var app = new InfiniFrameWebApplication { - Logger = NullLogger.Instance, - WebApp = webApp, - LazyWindow = lazyWindow - }; - - // Act - InfiniFrameWebApplication result = app.UseAutoServerClose(); - - // Assert - await Assert.That(result).IsEqualTo(app); - await Assert.That(mockEvents.EventsStore.Closing.Snapshot.Length).IsGreaterThan(0); - } - - [Test] - public async Task UseAutoServerClose_ClosingHandler_ShouldReturnFalse() { - // Arrange - (_, IInfiniFrameWindow mockWindow) = CreateMockWindow(); - IInfiniFrameEvents mockEvents = mockWindow.Events; - - WebApplicationBuilder webAppBuilder = WebApplication.CreateBuilder(); - WebApplication webApp = webAppBuilder.Build(); - - var lazyWindow = new Lazy(() => mockWindow); - _ = lazyWindow.Value; - - var app = new InfiniFrameWebApplication { - Logger = NullLogger.Instance, - WebApp = webApp, - LazyWindow = lazyWindow - }; - - // Act - app.UseAutoServerClose(); - Func? capturedHandler = mockEvents.EventsStore.Closing.Snapshot.LastOrDefault(); - WindowClosingResult result = capturedHandler?.Invoke(mockWindow, EventArgs.Empty) ?? WindowClosingResult.Cancel; - - // Assert - await Assert.That(capturedHandler).IsNotNull(); - await Assert.That(result).IsEqualTo(WindowClosingResult.Close); - } - - [Test] - public async Task UseAutoServerClose_ClosingHandler_ShouldInitiateStopAsync() { - // Arrange - (_, IInfiniFrameWindow mockWindow) = CreateMockWindow(); - IInfiniFrameEvents mockEvents = mockWindow.Events; - - WebApplicationBuilder webAppBuilder = WebApplication.CreateBuilder(); - WebApplication webApp = webAppBuilder.Build(); - - var lazyWindow = new Lazy(() => mockWindow); - _ = lazyWindow.Value; - - var app = new InfiniFrameWebApplication { - Logger = NullLogger.Instance, - WebApp = webApp, - LazyWindow = lazyWindow - }; - - app.UseAutoServerClose(); - - // Act - Func? capturedHandler = mockEvents.EventsStore.Closing.Snapshot.LastOrDefault(); - capturedHandler?.Invoke(mockWindow, EventArgs.Empty); - - var appLifetime = webApp.Services.GetRequiredService(); - DateTime deadline = DateTime.UtcNow.AddSeconds(2); - while (!appLifetime.ApplicationStopping.IsCancellationRequested && DateTime.UtcNow < deadline) { - await Task.Delay(50); - } - - if (!appLifetime.ApplicationStopping.IsCancellationRequested) { - Console.WriteLine("Timed out waiting for ApplicationStopping after closing handler invocation."); - } - - // Assert - // The web app should be in the process of stopping - await Assert.That(appLifetime.ApplicationStopping.IsCancellationRequested) - .IsTrue(); - } - - [Test] - public async Task Window_Property_ShouldReturnLazyValue() { - // Arrange - (_, IInfiniFrameWindow mockWindow) = CreateMockWindow(); - WebApplicationBuilder webAppBuilder = WebApplication.CreateBuilder(); - WebApplication webApp = webAppBuilder.Build(); - - var lazyWindow = new Lazy(() => mockWindow); - - var app = new InfiniFrameWebApplication { - Logger = NullLogger.Instance, - WebApp = webApp, - LazyWindow = lazyWindow - }; - - // Act - IInfiniFrameWindow window = app.Window; - - // Assert - await Assert.That(window).IsEqualTo(mockWindow); - await Assert.That(lazyWindow.IsValueCreated).IsTrue(); - } -} diff --git a/tests/InfiniTests.InfiniFrame.WebServer/InfiniTests.InfiniFrame.WebServer.csproj b/tests/InfiniTests.InfiniFrame.WebServer/InfiniTests.InfiniFrame.WebServer.csproj index 7c1276963..2b5e00899 100644 --- a/tests/InfiniTests.InfiniFrame.WebServer/InfiniTests.InfiniFrame.WebServer.csproj +++ b/tests/InfiniTests.InfiniFrame.WebServer/InfiniTests.InfiniFrame.WebServer.csproj @@ -8,10 +8,10 @@ - + - + diff --git a/tests/InfiniTests.InfiniFrame.WebServer/MultiWindowWebServerTests.cs b/tests/InfiniTests.InfiniFrame.WebServer/MultiWindowWebServerTests.cs new file mode 100644 index 000000000..7dbd64bc8 --- /dev/null +++ b/tests/InfiniTests.InfiniFrame.WebServer/MultiWindowWebServerTests.cs @@ -0,0 +1,121 @@ +using InfiniFrame; +using InfiniFrame.Application; +using InfiniFrame.WebServer; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; +using Microsoft.AspNetCore.Hosting; + +namespace InfiniTests.InfiniFrame.WebServer; + +[NotInParallelInfiniTests] +public sealed class MultiWindowWebServerTests { + [Test] + [Timeout(60_000)] + public async Task UseWebServer_CanRunTwoIndependentIntegrations(CancellationToken ct = default) { + if (!OperatingSystem.IsWindows() || Environment.Version.Major < 10) return; + + int firstPort = PortUtils.GetOpenPortValue(); + int secondPort = PortUtils.GetOpenPortValue(); + await using InfiniFrameApplication application = InfiniFrameApplication.CreateBuilder() + .WithWindow("first", static _ => { }) + .WithWindow("second", static _ => { }) + .UseWebServer("first", server => server.WebHost.UseUrls($"http://127.0.0.1:{firstPort}")) + .UseWebServer("second", server => server.WebHost.UseUrls($"http://127.0.0.1:{secondPort}")) + .Build(); + + Task runTask = application.RunAsync(ct); + try { + for (int attempt = 0; attempt < 100; attempt++) { + if (runTask.IsFaulted) await runTask; + + Uri? firstUri = application.TryGetWindow("first")?.Features.PageNavigation.GetCurrentUri(); + Uri? secondUri = application.TryGetWindow("second")?.Features.PageNavigation.GetCurrentUri(); + if (firstUri?.Port == firstPort && secondUri?.Port == secondPort) break; + + await Task.Delay(100, ct); + } + + await Assert.That(application.GetWindow("first").Features.PageNavigation.GetCurrentUri()?.Port) + .IsEqualTo(firstPort); + await Assert.That(application.GetWindow("second").Features.PageNavigation.GetCurrentUri()?.Port) + .IsEqualTo(secondPort); + } + finally { + application.Shutdown(); + await runTask.WaitAsync(TimeSpan.FromSeconds(30), CancellationToken.None); + } + } + + [Test] + [Timeout(60_000)] + public async Task UseWebServer_UsesCommandLineUrls(CancellationToken ct = default) { + if (!OperatingSystem.IsWindows() || Environment.Version.Major < 10) return; + + int port = PortUtils.GetOpenPortValue(); + await using InfiniFrameApplication application = InfiniFrameApplication.CreateBuilder([ + "--urls", $"http://127.0.0.1:{port}" + ]) + .WithWindow("server", static _ => { }) + .UseWebServer("server", static _ => { }) + .Build(); + + Task runTask = application.RunAsync(ct); + try { + for (int attempt = 0; attempt < 100; attempt++) { + if (runTask.IsFaulted) await runTask; + + if (application.TryGetWindow("server")?.Features.PageNavigation.GetCurrentUri()?.Port == port) + break; + + await Task.Delay(100, ct); + } + + await Assert.That(application.GetWindow("server").Features.PageNavigation.GetCurrentUri()?.Port) + .IsEqualTo(port); + } + finally { + application.Shutdown(); + await runTask.WaitAsync(TimeSpan.FromSeconds(30), CancellationToken.None); + } + } + + [Test] + [Timeout(60_000)] + public async Task UseWebServer_AttachesTheStartedServerToSelectedWindows(CancellationToken ct = default) { + if (!OperatingSystem.IsWindows() || Environment.Version.Major < 10) return; + + await using InfiniFrameApplication application = InfiniFrameApplication.CreateBuilder() + .WithWindow("main", builder => builder.SetTitle("Main")) + .WithWindow("secondary", builder => builder.SetTitle("Secondary")) + .WithWindow("plain", builder => builder.SetStartPageContent("Plain")) + .UseWebServer(["main", "secondary"], server => + server.WebHost.UseUrls("http://127.0.0.1:0")) + .Build(); + + Task runTask = application.RunAsync(ct); + try { + for (int attempt = 0; attempt < 100; attempt++) { + if (runTask.IsFaulted) await runTask; + + Uri? mainUri = application.TryGetWindow("main")?.Features.PageNavigation.GetCurrentUri(); + Uri? secondaryUri = application.TryGetWindow("secondary")?.Features.PageNavigation.GetCurrentUri(); + if (mainUri?.Scheme == Uri.UriSchemeHttp && secondaryUri?.Scheme == Uri.UriSchemeHttp) + break; + + await Task.Delay(100, ct); + } + + Uri? mainResult = application.GetWindow("main").Features.PageNavigation.GetCurrentUri(); + Uri? secondaryResult = application.GetWindow("secondary").Features.PageNavigation.GetCurrentUri(); + + await Assert.That(mainResult?.Scheme).IsEqualTo(Uri.UriSchemeHttp); + await Assert.That(secondaryResult?.Scheme).IsEqualTo(Uri.UriSchemeHttp); + await Assert.That(mainResult?.Port).IsEqualTo(secondaryResult?.Port); + await Assert.That(application.GetWindow("plain")).IsNotNull(); + } + finally { + application.Shutdown(); + await runTask.WaitAsync(TimeSpan.FromSeconds(30), CancellationToken.None); + } + } +} diff --git a/tests/InfiniTests.InfiniFrame.WebServer/TestSettings.cs b/tests/InfiniTests.InfiniFrame.WebServer/TestSettings.cs index f7b615e12..1084ea750 100644 --- a/tests/InfiniTests.InfiniFrame.WebServer/TestSettings.cs +++ b/tests/InfiniTests.InfiniFrame.WebServer/TestSettings.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniTests; +using InfiniTests.TestSupport.Attributes; // --------------------------------------------------------------------------------------------------------------------- // Code diff --git a/tests/InfiniTests.InfiniFrame/BrowserInfiniFrameWindowBuilderFeatureTests.cs b/tests/InfiniTests.InfiniFrame.Window/BrowserInfiniFrameWindowBuilderFeatureTests.cs similarity index 93% rename from tests/InfiniTests.InfiniFrame/BrowserInfiniFrameWindowBuilderFeatureTests.cs rename to tests/InfiniTests.InfiniFrame.Window/BrowserInfiniFrameWindowBuilderFeatureTests.cs index ce1ca7f13..6ff651e8c 100644 --- a/tests/InfiniTests.InfiniFrame/BrowserInfiniFrameWindowBuilderFeatureTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/BrowserInfiniFrameWindowBuilderFeatureTests.cs @@ -1,10 +1,10 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Features.Browser; -namespace InfiniTests.InfiniFrame; +namespace InfiniTests.InfiniFrame.Window; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -30,7 +30,6 @@ public async Task DefaultValues_AreCorrect(CancellationToken ct = default) { await Assert.That(feature.IsBrowserShortcutsEnabled).IsTrue(); await Assert.That(feature.BrowserControlInitParameters).IsNull(); await Assert.That(feature.TemporaryFilesPath).IsNotEmpty(); - await Assert.That(feature.WebView2RuntimePath).IsNull(); } [Test] @@ -99,9 +98,8 @@ public async Task ApplyToNativeParameters_SetsAllValues(CancellationToken ct = d feature.EnableBrowserShortcuts(false); feature.SetBrowserControlInitParameters("init-params"); feature.SetTemporaryFilesPath("/tmp/test"); - feature.SetWebView2RuntimePath("/runtime/path"); - var parameters = new InfiniFrameNativeParameters(); + var parameters = new InfiniFrameNativeWindowParameters(); // Act feature.ApplyToNativeParameters(ref parameters); @@ -121,6 +119,5 @@ public async Task ApplyToNativeParameters_SetsAllValues(CancellationToken ct = d await Assert.That(parameters.BrowserShortcutsEnabled).IsFalse(); await Assert.That(parameters.BrowserControlInitParameters).IsEqualTo("init-params"); await Assert.That(parameters.TemporaryFilesPath).IsEqualTo(Path.GetFullPath("/tmp/test")); - await Assert.That(parameters.WebView2RuntimePath).IsEqualTo(Path.GetFullPath("/runtime/path")); } } diff --git a/tests/InfiniTests.InfiniFrame/DecorationsInfiniFrameWindowBuilderFeatureTests.cs b/tests/InfiniTests.InfiniFrame.Window/DecorationsInfiniFrameWindowBuilderFeatureTests.cs similarity index 77% rename from tests/InfiniTests.InfiniFrame/DecorationsInfiniFrameWindowBuilderFeatureTests.cs rename to tests/InfiniTests.InfiniFrame.Window/DecorationsInfiniFrameWindowBuilderFeatureTests.cs index c884798ba..b8ab0c4db 100644 --- a/tests/InfiniTests.InfiniFrame/DecorationsInfiniFrameWindowBuilderFeatureTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/DecorationsInfiniFrameWindowBuilderFeatureTests.cs @@ -1,10 +1,10 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Features.Decorations; -namespace InfiniTests.InfiniFrame; +namespace InfiniTests.InfiniFrame.Window; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -21,7 +21,6 @@ public async Task DefaultValues_AreCorrect(CancellationToken ct = default) { await Assert.That(feature.BackgroundColor).IsNull(); await Assert.That(feature.Title).IsEqualTo("InfiniFrame"); await Assert.That(feature.IconFilePath).IsNull(); - await Assert.That(feature.WindowsAppUserModelId).IsNull(); await Assert.That(feature.LimitLinuxWindowTitleLength).IsFalse(); } @@ -85,18 +84,6 @@ public async Task SetIconFile_SetsValue(CancellationToken ct = default) { await Assert.That(feature.IconFilePath).IsEqualTo("/path/to/icon.png"); } - [Test] - public async Task SetWindowsAppUserModelId_SetsValue(CancellationToken ct = default) { - // Arrange - var feature = new DecorationsInfiniFrameWindowBuilderFeature(); - - // Act - feature.SetWindowsAppUserModelId("com.myapp"); - - // Assert - await Assert.That(feature.WindowsAppUserModelId).IsEqualTo("com.myapp"); - } - [Test] public async Task SetLimitLinuxWindowTitleLength_TogglesValue(CancellationToken ct = default) { // Arrange @@ -117,7 +104,7 @@ public async Task ApplyToNativeParameters_SetsChromelessAndTransparent(Cancellat feature.SetTransparent(true); feature.SetTitle("Test Title"); - var parameters = new InfiniFrameNativeParameters(); + var parameters = new InfiniFrameNativeWindowParameters(); // Act feature.ApplyToNativeParameters(ref parameters); @@ -128,18 +115,4 @@ public async Task ApplyToNativeParameters_SetsChromelessAndTransparent(Cancellat await Assert.That(parameters.Title).IsEqualTo("Test Title"); } - [Test] - public async Task ApplyToNativeParameters_SetsWindowsAppUserModelId(CancellationToken ct = default) { - // Arrange - var feature = new DecorationsInfiniFrameWindowBuilderFeature(); - feature.SetWindowsAppUserModelId("my.app.id"); - - var parameters = new InfiniFrameNativeParameters(); - - // Act - feature.ApplyToNativeParameters(ref parameters); - - // Assert - await Assert.That(parameters.WindowsAppUserModelId).IsEqualTo("my.app.id"); - } } diff --git a/tests/InfiniTests.InfiniFrame/Events/CustomSchemeResponseValidatorTests.cs b/tests/InfiniTests.InfiniFrame.Window/Events/CustomSchemeResponseValidatorTests.cs similarity index 98% rename from tests/InfiniTests.InfiniFrame/Events/CustomSchemeResponseValidatorTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Events/CustomSchemeResponseValidatorTests.cs index c6490c044..fc3b429ad 100644 --- a/tests/InfiniTests.InfiniFrame/Events/CustomSchemeResponseValidatorTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Events/CustomSchemeResponseValidatorTests.cs @@ -3,7 +3,7 @@ // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame.Utilities; -namespace InfiniTests.InfiniFrame.Events; +namespace InfiniTests.InfiniFrame.Window.Events; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Events/InfiniFrameEventsStoreTests.cs b/tests/InfiniTests.InfiniFrame.Window/Events/InfiniFrameEventsStoreTests.cs similarity index 99% rename from tests/InfiniTests.InfiniFrame/Events/InfiniFrameEventsStoreTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Events/InfiniFrameEventsStoreTests.cs index cf89c9ff1..7e487baa2 100644 --- a/tests/InfiniTests.InfiniFrame/Events/InfiniFrameEventsStoreTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Events/InfiniFrameEventsStoreTests.cs @@ -4,8 +4,10 @@ using System.Drawing; using InfiniFrame; using InfiniFrame.Debugging; +using InfiniFrame.Window.Events; +using InfiniTests.TestSupport; -namespace InfiniTests.InfiniFrame.Events; +namespace InfiniTests.InfiniFrame.Window.Events; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Features/Debugging/EndpointStatusResolverTests.cs b/tests/InfiniTests.InfiniFrame.Window/Features/Debugging/EndpointStatusResolverTests.cs similarity index 98% rename from tests/InfiniTests.InfiniFrame/Features/Debugging/EndpointStatusResolverTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Features/Debugging/EndpointStatusResolverTests.cs index b83b3c81f..480d7d48a 100644 --- a/tests/InfiniTests.InfiniFrame/Features/Debugging/EndpointStatusResolverTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Features/Debugging/EndpointStatusResolverTests.cs @@ -4,7 +4,7 @@ using InfiniFrame.Debugging; using InfiniFrame.Utilities; -namespace InfiniTests.InfiniFrame.Features.Debugging; +namespace InfiniTests.InfiniFrame.Window.Features.Debugging; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Features/Decorations/ColorUtilityTests.cs b/tests/InfiniTests.InfiniFrame.Window/Features/Decorations/ColorUtilityTests.cs similarity index 99% rename from tests/InfiniTests.InfiniFrame/Features/Decorations/ColorUtilityTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Features/Decorations/ColorUtilityTests.cs index 22e44bde4..40c69be2f 100644 --- a/tests/InfiniTests.InfiniFrame/Features/Decorations/ColorUtilityTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Features/Decorations/ColorUtilityTests.cs @@ -3,7 +3,7 @@ // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame.Utilities; -namespace InfiniTests.InfiniFrame.Features.Decorations; +namespace InfiniTests.InfiniFrame.Window.Features.Decorations; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Features/Menu/MenuItemTreeHelperTests.cs b/tests/InfiniTests.InfiniFrame.Window/Features/Menu/MenuItemTreeHelperTests.cs similarity index 98% rename from tests/InfiniTests.InfiniFrame/Features/Menu/MenuItemTreeHelperTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Features/Menu/MenuItemTreeHelperTests.cs index 7f4089ff8..8821b3281 100644 --- a/tests/InfiniTests.InfiniFrame/Features/Menu/MenuItemTreeHelperTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Features/Menu/MenuItemTreeHelperTests.cs @@ -5,7 +5,7 @@ using InfiniFrame; using InfiniFrame.Utilities; -namespace InfiniTests.InfiniFrame.Features.Menu; +namespace InfiniTests.InfiniFrame.Window.Features.Menu; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Features/Position/PositionCalculationsTests.cs b/tests/InfiniTests.InfiniFrame.Window/Features/Position/PositionCalculationsTests.cs similarity index 99% rename from tests/InfiniTests.InfiniFrame/Features/Position/PositionCalculationsTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Features/Position/PositionCalculationsTests.cs index d6c049445..86fd90499 100644 --- a/tests/InfiniTests.InfiniFrame/Features/Position/PositionCalculationsTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Features/Position/PositionCalculationsTests.cs @@ -4,7 +4,7 @@ using System.Drawing; using InfiniFrame.Utilities; -namespace InfiniTests.InfiniFrame.Features.Position; +namespace InfiniTests.InfiniFrame.Window.Features.Position; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Features/Size/SizeCalculationsTests.cs b/tests/InfiniTests.InfiniFrame.Window/Features/Size/SizeCalculationsTests.cs similarity index 99% rename from tests/InfiniTests.InfiniFrame/Features/Size/SizeCalculationsTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Features/Size/SizeCalculationsTests.cs index 94e31b268..fb1882ea3 100644 --- a/tests/InfiniTests.InfiniFrame/Features/Size/SizeCalculationsTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Features/Size/SizeCalculationsTests.cs @@ -4,7 +4,7 @@ using InfiniFrame; using InfiniFrame.Utilities; -namespace InfiniTests.InfiniFrame.Features.Size; +namespace InfiniTests.InfiniFrame.Window.Features.Size; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/InfiniFrameEventsStoreTests.cs b/tests/InfiniTests.InfiniFrame.Window/InfiniFrameEventsStoreTests.cs similarity index 98% rename from tests/InfiniTests.InfiniFrame/InfiniFrameEventsStoreTests.cs rename to tests/InfiniTests.InfiniFrame.Window/InfiniFrameEventsStoreTests.cs index e5ad28d42..c86f09c42 100644 --- a/tests/InfiniTests.InfiniFrame/InfiniFrameEventsStoreTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/InfiniFrameEventsStoreTests.cs @@ -2,8 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniFrame.Window.Events; -namespace InfiniTests.InfiniFrame; +namespace InfiniTests.InfiniFrame.Window; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/InfiniFrameWindowFeaturesFactoryTests.cs b/tests/InfiniTests.InfiniFrame.Window/InfiniFrameWindowFeaturesFactoryTests.cs similarity index 94% rename from tests/InfiniTests.InfiniFrame/InfiniFrameWindowFeaturesFactoryTests.cs rename to tests/InfiniTests.InfiniFrame.Window/InfiniFrameWindowFeaturesFactoryTests.cs index c838cfada..b2b8b9119 100644 --- a/tests/InfiniTests.InfiniFrame/InfiniFrameWindowFeaturesFactoryTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/InfiniFrameWindowFeaturesFactoryTests.cs @@ -1,10 +1,10 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame; +using InfiniFrame.Window; using Microsoft.Extensions.DependencyInjection; -namespace InfiniTests.InfiniFrame; +namespace InfiniTests.InfiniFrame.Window; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/InfiniTests.InfiniFrame.csproj b/tests/InfiniTests.InfiniFrame.Window/InfiniTests.InfiniFrame.Window.csproj similarity index 54% rename from tests/InfiniTests.InfiniFrame/InfiniTests.InfiniFrame.csproj rename to tests/InfiniTests.InfiniFrame.Window/InfiniTests.InfiniFrame.Window.csproj index 41ecca766..3d0ba9cd3 100644 --- a/tests/InfiniTests.InfiniFrame/InfiniTests.InfiniFrame.csproj +++ b/tests/InfiniTests.InfiniFrame.Window/InfiniTests.InfiniFrame.Window.csproj @@ -8,9 +8,13 @@ - + + + + + - + diff --git a/tests/InfiniTests.InfiniFrame.Window/InfiniTests.InfiniFrame.Window.csproj.DotSettings b/tests/InfiniTests.InfiniFrame.Window/InfiniTests.InfiniFrame.Window.csproj.DotSettings new file mode 100644 index 000000000..abc858129 --- /dev/null +++ b/tests/InfiniTests.InfiniFrame.Window/InfiniTests.InfiniFrame.Window.csproj.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file diff --git a/tests/InfiniTests.InfiniFrame/Interop/InteropEnvelopeProtocolCreateTests.cs b/tests/InfiniTests.InfiniFrame.Window/Interop/InteropEnvelopeProtocolCreateTests.cs similarity index 97% rename from tests/InfiniTests.InfiniFrame/Interop/InteropEnvelopeProtocolCreateTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Interop/InteropEnvelopeProtocolCreateTests.cs index 2e56436cc..5d6127bac 100644 --- a/tests/InfiniTests.InfiniFrame/Interop/InteropEnvelopeProtocolCreateTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Interop/InteropEnvelopeProtocolCreateTests.cs @@ -1,9 +1,9 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame.Interop; +using InfiniFrame.Js.Interop; -namespace InfiniTests.InfiniFrame.Interop; +namespace InfiniTests.InfiniFrame.Window.Interop; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Interop/InteropEnvelopeProtocolEdgeCaseTests.cs b/tests/InfiniTests.InfiniFrame.Window/Interop/InteropEnvelopeProtocolEdgeCaseTests.cs similarity index 98% rename from tests/InfiniTests.InfiniFrame/Interop/InteropEnvelopeProtocolEdgeCaseTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Interop/InteropEnvelopeProtocolEdgeCaseTests.cs index d3a8ead0a..4faa209a1 100644 --- a/tests/InfiniTests.InfiniFrame/Interop/InteropEnvelopeProtocolEdgeCaseTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Interop/InteropEnvelopeProtocolEdgeCaseTests.cs @@ -2,9 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using System.Text.Json; -using InfiniFrame.Interop; +using InfiniFrame.Js.Interop; -namespace InfiniTests.InfiniFrame.Interop; +namespace InfiniTests.InfiniFrame.Window.Interop; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Interop/InteropEnvelopeProtocolTests.cs b/tests/InfiniTests.InfiniFrame.Window/Interop/InteropEnvelopeProtocolTests.cs similarity index 98% rename from tests/InfiniTests.InfiniFrame/Interop/InteropEnvelopeProtocolTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Interop/InteropEnvelopeProtocolTests.cs index 37eb6363d..35c9faef6 100644 --- a/tests/InfiniTests.InfiniFrame/Interop/InteropEnvelopeProtocolTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Interop/InteropEnvelopeProtocolTests.cs @@ -2,9 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using System.Text.Json; -using InfiniFrame.Interop; +using InfiniFrame.Js.Interop; -namespace InfiniTests.InfiniFrame.Interop; +namespace InfiniTests.InfiniFrame.Window.Interop; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Interop/RegisterWindowCreatedUtilityTests.cs b/tests/InfiniTests.InfiniFrame.Window/Interop/RegisterWindowCreatedUtilityTests.cs similarity index 91% rename from tests/InfiniTests.InfiniFrame/Interop/RegisterWindowCreatedUtilityTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Interop/RegisterWindowCreatedUtilityTests.cs index ff8a840a6..90c2275e7 100644 --- a/tests/InfiniTests.InfiniFrame/Interop/RegisterWindowCreatedUtilityTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Interop/RegisterWindowCreatedUtilityTests.cs @@ -1,13 +1,15 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame; -using InfiniFrame.Interop; -using InfiniFrame.NativeBridge.Parameters; -using InfiniTests.Substitutes; +using InfiniFrame.Js.Interop; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniFrame.Window.Events; +using InfiniFrame.Window.Interop; +using InfiniTests.TestSupport.Substitutes; using Microsoft.Extensions.Logging.Abstractions; -namespace InfiniTests.InfiniFrame.Interop; +namespace InfiniTests.InfiniFrame.Window.Interop; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -23,7 +25,7 @@ public async Task Registration_IsGatedByWindowReadyHandshake(CancellationToken c RecordingInfiniFrameWindowSubstitute window = new RecordingInfiniFrameWindowSubstitute() .BindToBuilder(builder); - var nativeParameters = default(InfiniFrameNativeParameters); + var nativeParameters = default(InfiniFrameNativeWindowParameters); events.AssignToNativeParameters(ref nativeParameters); events.AssignToWindow(window.Window); @@ -60,7 +62,7 @@ public async Task Registration_IsIdempotentAcrossRepeatedReadyMessages(Cancellat RecordingInfiniFrameWindowSubstitute window = new RecordingInfiniFrameWindowSubstitute() .BindToBuilder(builder); - var nativeParameters = default(InfiniFrameNativeParameters); + var nativeParameters = default(InfiniFrameNativeWindowParameters); events.AssignToNativeParameters(ref nativeParameters); events.AssignToWindow(window.Window); @@ -87,7 +89,7 @@ public async Task Registration_AcknowledgementIsSentAfterRegistrations(Cancellat RecordingInfiniFrameWindowSubstitute window = new RecordingInfiniFrameWindowSubstitute() .BindToBuilder(builder); - var nativeParameters = default(InfiniFrameNativeParameters); + var nativeParameters = default(InfiniFrameNativeWindowParameters); events.AssignToNativeParameters(ref nativeParameters); events.AssignToWindow(window.Window); diff --git a/tests/InfiniTests.InfiniFrame/Interop/WindowRegistrationStateMachineTests.cs b/tests/InfiniTests.InfiniFrame.Window/Interop/WindowRegistrationStateMachineTests.cs similarity index 97% rename from tests/InfiniTests.InfiniFrame/Interop/WindowRegistrationStateMachineTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Interop/WindowRegistrationStateMachineTests.cs index b87e86e87..28ee084dc 100644 --- a/tests/InfiniTests.InfiniFrame/Interop/WindowRegistrationStateMachineTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Interop/WindowRegistrationStateMachineTests.cs @@ -1,9 +1,9 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame.Interop; +using InfiniFrame.Window.Interop; -namespace InfiniTests.InfiniFrame.Interop; +namespace InfiniTests.InfiniFrame.Window.Interop; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Security/InfiniFrameUriSecurityPolicyAdditionalTests.cs b/tests/InfiniTests.InfiniFrame.Window/Security/InfiniFrameUriSecurityPolicyAdditionalTests.cs similarity index 99% rename from tests/InfiniTests.InfiniFrame/Security/InfiniFrameUriSecurityPolicyAdditionalTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Security/InfiniFrameUriSecurityPolicyAdditionalTests.cs index 61e87a65c..6d179d469 100644 --- a/tests/InfiniTests.InfiniFrame/Security/InfiniFrameUriSecurityPolicyAdditionalTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Security/InfiniFrameUriSecurityPolicyAdditionalTests.cs @@ -3,7 +3,7 @@ // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame.Security; -namespace InfiniTests.InfiniFrame.Security; +namespace InfiniTests.InfiniFrame.Window.Security; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Security/InfiniFrameUriSecurityPolicyBuilderExtensionsTests.cs b/tests/InfiniTests.InfiniFrame.Window/Security/InfiniFrameUriSecurityPolicyBuilderExtensionsTests.cs similarity index 99% rename from tests/InfiniTests.InfiniFrame/Security/InfiniFrameUriSecurityPolicyBuilderExtensionsTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Security/InfiniFrameUriSecurityPolicyBuilderExtensionsTests.cs index 1bfece542..be2308ae0 100644 --- a/tests/InfiniTests.InfiniFrame/Security/InfiniFrameUriSecurityPolicyBuilderExtensionsTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Security/InfiniFrameUriSecurityPolicyBuilderExtensionsTests.cs @@ -3,8 +3,9 @@ // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; using InfiniFrame.Security; +using InfiniTests.TestSupport; -namespace InfiniTests.InfiniFrame.Security; +namespace InfiniTests.InfiniFrame.Window.Security; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Security/InfiniFrameUriSecurityPolicyBuilderTests.cs b/tests/InfiniTests.InfiniFrame.Window/Security/InfiniFrameUriSecurityPolicyBuilderTests.cs similarity index 99% rename from tests/InfiniTests.InfiniFrame/Security/InfiniFrameUriSecurityPolicyBuilderTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Security/InfiniFrameUriSecurityPolicyBuilderTests.cs index 9ceca3d66..194e48209 100644 --- a/tests/InfiniTests.InfiniFrame/Security/InfiniFrameUriSecurityPolicyBuilderTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Security/InfiniFrameUriSecurityPolicyBuilderTests.cs @@ -3,7 +3,7 @@ // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame.Security; -namespace InfiniTests.InfiniFrame.Security; +namespace InfiniTests.InfiniFrame.Window.Security; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Security/InfiniFrameUriSecurityPolicyRegistryTests.cs b/tests/InfiniTests.InfiniFrame.Window/Security/InfiniFrameUriSecurityPolicyRegistryTests.cs similarity index 98% rename from tests/InfiniTests.InfiniFrame/Security/InfiniFrameUriSecurityPolicyRegistryTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Security/InfiniFrameUriSecurityPolicyRegistryTests.cs index 4f503e67c..bdd3b67a9 100644 --- a/tests/InfiniTests.InfiniFrame/Security/InfiniFrameUriSecurityPolicyRegistryTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Security/InfiniFrameUriSecurityPolicyRegistryTests.cs @@ -1,11 +1,11 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame; using InfiniFrame.Security; -using InfiniTests.Substitutes; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport.Substitutes; -namespace InfiniTests.InfiniFrame.Security; +namespace InfiniTests.InfiniFrame.Window.Security; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Security/InfiniFrameUriSecurityPolicyTests.cs b/tests/InfiniTests.InfiniFrame.Window/Security/InfiniFrameUriSecurityPolicyTests.cs similarity index 98% rename from tests/InfiniTests.InfiniFrame/Security/InfiniFrameUriSecurityPolicyTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Security/InfiniFrameUriSecurityPolicyTests.cs index 98af67c45..9f058684c 100644 --- a/tests/InfiniTests.InfiniFrame/Security/InfiniFrameUriSecurityPolicyTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Security/InfiniFrameUriSecurityPolicyTests.cs @@ -1,11 +1,11 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame; using InfiniFrame.Security; -using InfiniTests.Substitutes; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport.Substitutes; -namespace InfiniTests.InfiniFrame.Security; +namespace InfiniTests.InfiniFrame.Window.Security; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/ServiceCollectionExtensionsTests.cs b/tests/InfiniTests.InfiniFrame.Window/ServiceCollectionExtensionsTests.cs similarity index 92% rename from tests/InfiniTests.InfiniFrame/ServiceCollectionExtensionsTests.cs rename to tests/InfiniTests.InfiniFrame.Window/ServiceCollectionExtensionsTests.cs index 42148239a..ff9249159 100644 --- a/tests/InfiniTests.InfiniFrame/ServiceCollectionExtensionsTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/ServiceCollectionExtensionsTests.cs @@ -3,10 +3,11 @@ // --------------------------------------------------------------------------------------------------------------------- using FluentValidation; using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window; using Microsoft.Extensions.DependencyInjection; -namespace InfiniTests.InfiniFrame; +namespace InfiniTests.InfiniFrame.Window; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -25,7 +26,7 @@ public async Task AddInfiniFrame_RegistersAllServices(CancellationToken ct = def // Assert await Assert.That(provider.GetService()).IsNotNull(); await Assert.That(provider.GetService()).IsNotNull(); - await Assert.That(provider.GetService>()).IsNotNull(); + await Assert.That(provider.GetService>()).IsNotNull(); await Assert.That(provider.GetService()).IsNotNull(); } diff --git a/tests/InfiniTests.InfiniFrame/SizeInfiniFrameWindowBuilderFeatureTests.cs b/tests/InfiniTests.InfiniFrame.Window/SizeInfiniFrameWindowBuilderFeatureTests.cs similarity index 90% rename from tests/InfiniTests.InfiniFrame/SizeInfiniFrameWindowBuilderFeatureTests.cs rename to tests/InfiniTests.InfiniFrame.Window/SizeInfiniFrameWindowBuilderFeatureTests.cs index 716e56344..ec43c63d8 100644 --- a/tests/InfiniTests.InfiniFrame/SizeInfiniFrameWindowBuilderFeatureTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/SizeInfiniFrameWindowBuilderFeatureTests.cs @@ -2,9 +2,11 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniFrame.Window.Features.Size; +using InfiniTests.TestSupport; using Microsoft.Extensions.Logging; -namespace InfiniTests.InfiniFrame; +namespace InfiniTests.InfiniFrame.Window; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/StaticAssets/DisposableCompositeFileProviderTests.cs b/tests/InfiniTests.InfiniFrame.Window/StaticAssets/DisposableCompositeFileProviderTests.cs similarity index 99% rename from tests/InfiniTests.InfiniFrame/StaticAssets/DisposableCompositeFileProviderTests.cs rename to tests/InfiniTests.InfiniFrame.Window/StaticAssets/DisposableCompositeFileProviderTests.cs index 4a17aaab4..08f103741 100644 --- a/tests/InfiniTests.InfiniFrame/StaticAssets/DisposableCompositeFileProviderTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/StaticAssets/DisposableCompositeFileProviderTests.cs @@ -6,7 +6,7 @@ using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Primitives; -namespace InfiniTests.InfiniFrame.StaticAssets; +namespace InfiniTests.InfiniFrame.Window.StaticAssets; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/StaticAssets/FileProviderFactoryAdditionalTests.cs b/tests/InfiniTests.InfiniFrame.Window/StaticAssets/FileProviderFactoryAdditionalTests.cs similarity index 98% rename from tests/InfiniTests.InfiniFrame/StaticAssets/FileProviderFactoryAdditionalTests.cs rename to tests/InfiniTests.InfiniFrame.Window/StaticAssets/FileProviderFactoryAdditionalTests.cs index 7c7574cf5..38e068c00 100644 --- a/tests/InfiniTests.InfiniFrame/StaticAssets/FileProviderFactoryAdditionalTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/StaticAssets/FileProviderFactoryAdditionalTests.cs @@ -5,7 +5,7 @@ using InfiniFrame.StaticAssets; using Microsoft.Extensions.FileProviders; -namespace InfiniTests.InfiniFrame.StaticAssets; +namespace InfiniTests.InfiniFrame.Window.StaticAssets; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/StaticAssets/FileProviderFactoryTests.cs b/tests/InfiniTests.InfiniFrame.Window/StaticAssets/FileProviderFactoryTests.cs similarity index 97% rename from tests/InfiniTests.InfiniFrame/StaticAssets/FileProviderFactoryTests.cs rename to tests/InfiniTests.InfiniFrame.Window/StaticAssets/FileProviderFactoryTests.cs index fb09822c5..5bb9fd79d 100644 --- a/tests/InfiniTests.InfiniFrame/StaticAssets/FileProviderFactoryTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/StaticAssets/FileProviderFactoryTests.cs @@ -5,7 +5,7 @@ using InfiniFrame.StaticAssets; using Microsoft.Extensions.FileProviders; -namespace InfiniTests.InfiniFrame.StaticAssets; +namespace InfiniTests.InfiniFrame.Window.StaticAssets; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/StaticAssets/InfiniFrameStaticAssetsTests.cs b/tests/InfiniTests.InfiniFrame.Window/StaticAssets/InfiniFrameStaticAssetsTests.cs similarity index 98% rename from tests/InfiniTests.InfiniFrame/StaticAssets/InfiniFrameStaticAssetsTests.cs rename to tests/InfiniTests.InfiniFrame.Window/StaticAssets/InfiniFrameStaticAssetsTests.cs index 0955b9348..c73aed2e8 100644 --- a/tests/InfiniTests.InfiniFrame/StaticAssets/InfiniFrameStaticAssetsTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/StaticAssets/InfiniFrameStaticAssetsTests.cs @@ -7,7 +7,7 @@ using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Primitives; -namespace InfiniTests.InfiniFrame.StaticAssets; +namespace InfiniTests.InfiniFrame.Window.StaticAssets; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/StaticAssets/StaticAssetSchemeHandlerAdditionalTests.cs b/tests/InfiniTests.InfiniFrame.Window/StaticAssets/StaticAssetSchemeHandlerAdditionalTests.cs similarity index 99% rename from tests/InfiniTests.InfiniFrame/StaticAssets/StaticAssetSchemeHandlerAdditionalTests.cs rename to tests/InfiniTests.InfiniFrame.Window/StaticAssets/StaticAssetSchemeHandlerAdditionalTests.cs index 41f78e6bf..46cb82025 100644 --- a/tests/InfiniTests.InfiniFrame/StaticAssets/StaticAssetSchemeHandlerAdditionalTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/StaticAssets/StaticAssetSchemeHandlerAdditionalTests.cs @@ -6,7 +6,7 @@ using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Primitives; -namespace InfiniTests.InfiniFrame.StaticAssets; +namespace InfiniTests.InfiniFrame.Window.StaticAssets; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/StaticAssets/StaticAssetSchemeHandlerTests.cs b/tests/InfiniTests.InfiniFrame.Window/StaticAssets/StaticAssetSchemeHandlerTests.cs similarity index 98% rename from tests/InfiniTests.InfiniFrame/StaticAssets/StaticAssetSchemeHandlerTests.cs rename to tests/InfiniTests.InfiniFrame.Window/StaticAssets/StaticAssetSchemeHandlerTests.cs index 5dbcafdd5..a544bdec7 100644 --- a/tests/InfiniTests.InfiniFrame/StaticAssets/StaticAssetSchemeHandlerTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/StaticAssets/StaticAssetSchemeHandlerTests.cs @@ -6,7 +6,7 @@ using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Primitives; -namespace InfiniTests.InfiniFrame.StaticAssets; +namespace InfiniTests.InfiniFrame.Window.StaticAssets; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/TestSettings.cs b/tests/InfiniTests.InfiniFrame.Window/TestSettings.cs similarity index 89% rename from tests/InfiniTests.InfiniFrame/TestSettings.cs rename to tests/InfiniTests.InfiniFrame.Window/TestSettings.cs index c22125583..c2d449fdc 100644 --- a/tests/InfiniTests.InfiniFrame/TestSettings.cs +++ b/tests/InfiniTests.InfiniFrame.Window/TestSettings.cs @@ -1,7 +1,8 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniTests; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; using TUnit.Core.Executors; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/WebMessaging/WindowFeatureDrawingJsonConverterTests.cs b/tests/InfiniTests.InfiniFrame.Window/WebMessaging/WindowFeatureDrawingJsonConverterTests.cs similarity index 98% rename from tests/InfiniTests.InfiniFrame/WebMessaging/WindowFeatureDrawingJsonConverterTests.cs rename to tests/InfiniTests.InfiniFrame.Window/WebMessaging/WindowFeatureDrawingJsonConverterTests.cs index c102a867a..2230e952a 100644 --- a/tests/InfiniTests.InfiniFrame/WebMessaging/WindowFeatureDrawingJsonConverterTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/WebMessaging/WindowFeatureDrawingJsonConverterTests.cs @@ -4,9 +4,9 @@ using System.Diagnostics.CodeAnalysis; using System.Drawing; using System.Text.Json; -using InfiniFrame; +using InfiniFrame.Window.Features.WebMessaging; -namespace InfiniTests.InfiniFrame.WebMessaging; +namespace InfiniTests.InfiniFrame.Window.WebMessaging; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/WebMessaging/WindowFeatureWebMessageHandlerTests.cs b/tests/InfiniTests.InfiniFrame.Window/WebMessaging/WindowFeatureWebMessageHandlerTests.cs similarity index 98% rename from tests/InfiniTests.InfiniFrame/WebMessaging/WindowFeatureWebMessageHandlerTests.cs rename to tests/InfiniTests.InfiniFrame.Window/WebMessaging/WindowFeatureWebMessageHandlerTests.cs index 585c5f5e3..9af09c55f 100644 --- a/tests/InfiniTests.InfiniFrame/WebMessaging/WindowFeatureWebMessageHandlerTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/WebMessaging/WindowFeatureWebMessageHandlerTests.cs @@ -2,9 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using System.Text.Json; -using InfiniFrame; +using InfiniFrame.Window.Features.WebMessaging.Handlers; -namespace InfiniTests.InfiniFrame.WebMessaging; +namespace InfiniTests.InfiniFrame.Window.WebMessaging; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Events/CustomSchemeResponseCorsPipelineTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Events/CustomSchemeResponseCorsPipelineTests.cs similarity index 99% rename from tests/InfiniTests.InfiniFrame/Window/Events/CustomSchemeResponseCorsPipelineTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Events/CustomSchemeResponseCorsPipelineTests.cs index c9f8d91cb..fc820e07e 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Events/CustomSchemeResponseCorsPipelineTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Events/CustomSchemeResponseCorsPipelineTests.cs @@ -6,6 +6,9 @@ using InfiniFrame.NativeBridge.Delegates; using Microsoft.Extensions.Logging.Abstractions; using System.Runtime.InteropServices; +using InfiniFrame.Window.Events; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Events; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Events/CustomSchemeResponsePipelineTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Events/CustomSchemeResponsePipelineTests.cs similarity index 98% rename from tests/InfiniTests.InfiniFrame/Window/Events/CustomSchemeResponsePipelineTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Events/CustomSchemeResponsePipelineTests.cs index 540c94590..03678ebc7 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Events/CustomSchemeResponsePipelineTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Events/CustomSchemeResponsePipelineTests.cs @@ -5,6 +5,9 @@ using System.Runtime.InteropServices; using InfiniFrame; using InfiniFrame.NativeBridge.Delegates; +using InfiniFrame.Window.Events; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; using Microsoft.Extensions.Logging.Abstractions; namespace InfiniTests.InfiniFrame.Window.Events; diff --git a/tests/InfiniTests.InfiniFrame/Window/Events/EventExceptionPolicyTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Events/EventExceptionPolicyTests.cs similarity index 98% rename from tests/InfiniTests.InfiniFrame/Window/Events/EventExceptionPolicyTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Events/EventExceptionPolicyTests.cs index 6a8c9ef64..9f23ca996 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Events/EventExceptionPolicyTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Events/EventExceptionPolicyTests.cs @@ -2,6 +2,7 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; namespace InfiniTests.InfiniFrame.Window.Events; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Events/InfiniFrameEventsCallbackLifetimeTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Events/InfiniFrameEventsCallbackLifetimeTests.cs similarity index 97% rename from tests/InfiniTests.InfiniFrame/Window/Events/InfiniFrameEventsCallbackLifetimeTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Events/InfiniFrameEventsCallbackLifetimeTests.cs index dd000d9a1..9140ccdf2 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Events/InfiniFrameEventsCallbackLifetimeTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Events/InfiniFrameEventsCallbackLifetimeTests.cs @@ -4,6 +4,9 @@ using System.Collections.Concurrent; using System.Reflection; using InfiniFrame; +using InfiniFrame.Window.Events; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; using Microsoft.Extensions.Logging.Abstractions; namespace InfiniTests.InfiniFrame.Window.Events; diff --git a/tests/InfiniTests.InfiniFrame/Window/Events/NavigationStartingCancellationTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Events/NavigationStartingCancellationTests.cs similarity index 98% rename from tests/InfiniTests.InfiniFrame/Window/Events/NavigationStartingCancellationTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Events/NavigationStartingCancellationTests.cs index 4d99f825d..6f9d0bb25 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Events/NavigationStartingCancellationTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Events/NavigationStartingCancellationTests.cs @@ -2,6 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Events; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Events/NavigationStartingEventTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Events/NavigationStartingEventTests.cs similarity index 99% rename from tests/InfiniTests.InfiniFrame/Window/Events/NavigationStartingEventTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Events/NavigationStartingEventTests.cs index 588e1fcee..b0ad76b7d 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Events/NavigationStartingEventTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Events/NavigationStartingEventTests.cs @@ -2,6 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Events; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Events/ParentChildWindowTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Events/ParentChildWindowTests.cs similarity index 96% rename from tests/InfiniTests.InfiniFrame/Window/Events/ParentChildWindowTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Events/ParentChildWindowTests.cs index 62e8a50f0..e71887084 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Events/ParentChildWindowTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Events/ParentChildWindowTests.cs @@ -2,7 +2,10 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniTests.Native; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using WindowsNative = InfiniTests.TestSupport.Native.WindowsNative; namespace InfiniTests.InfiniFrame.Window.Events; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Events/ReOpeningAnotherWindowTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Events/ReOpeningAnotherWindowTests.cs similarity index 95% rename from tests/InfiniTests.InfiniFrame/Window/Events/ReOpeningAnotherWindowTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Events/ReOpeningAnotherWindowTests.cs index 7d459ec83..a970c6c47 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Events/ReOpeningAnotherWindowTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Events/ReOpeningAnotherWindowTests.cs @@ -2,6 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Events; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Events/RegisterCustomSchemeHandlerTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Events/RegisterCustomSchemeHandlerTests.cs similarity index 94% rename from tests/InfiniTests.InfiniFrame/Window/Events/RegisterCustomSchemeHandlerTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Events/RegisterCustomSchemeHandlerTests.cs index 3b81031ac..55d7c6d02 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Events/RegisterCustomSchemeHandlerTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Events/RegisterCustomSchemeHandlerTests.cs @@ -3,7 +3,11 @@ // --------------------------------------------------------------------------------------------------------------------- using System.Runtime.InteropServices; using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniFrame.Window.Events; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; using Microsoft.Extensions.Logging.Abstractions; namespace InfiniTests.InfiniFrame.Window.Events; @@ -20,7 +24,7 @@ public async Task AtBuilderStage_RegistersSchemeInEventsStoreAndNativeParameters // Act builder.RegisterCustomSchemeHandler("app", EmptyHandler); - InfiniFrameNativeParameters nativeParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters nativeParameters = builder.CollectNativeParameters(); var events = new InfiniFrameEvents(builder.EventsStore, NullLogger.Instance); events.AssignToNativeParameters(ref nativeParameters); @@ -42,7 +46,7 @@ public async Task AtBuilderStage_ReRegisteringSameScheme_DoesNotDuplicateNativeP builder.RegisterCustomSchemeHandler("app", EmptyHandler); } - InfiniFrameNativeParameters nativeParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters nativeParameters = builder.CollectNativeParameters(); var events = new InfiniFrameEvents(builder.EventsStore, NullLogger.Instance); events.AssignToNativeParameters(ref nativeParameters); diff --git a/tests/InfiniTests.InfiniFrame/Window/Events/RegisterWebMessageReceivedHandlerTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Events/RegisterWebMessageReceivedHandlerTests.cs similarity index 97% rename from tests/InfiniTests.InfiniFrame/Window/Events/RegisterWebMessageReceivedHandlerTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Events/RegisterWebMessageReceivedHandlerTests.cs index 5e74566f1..50ee8de78 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Events/RegisterWebMessageReceivedHandlerTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Events/RegisterWebMessageReceivedHandlerTests.cs @@ -2,6 +2,10 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniFrame.Window.Builder; +using InfiniFrame.Window.Events; +using InfiniTests.TestSupport; + namespace InfiniTests.InfiniFrame.Window.Events; // --------------------------------------------------------------------------------------------------------------------- // Code diff --git a/tests/InfiniTests.InfiniFrame/Window/Events/WindowClosedEventTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Events/WindowClosedEventTests.cs similarity index 96% rename from tests/InfiniTests.InfiniFrame/Window/Events/WindowClosedEventTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Events/WindowClosedEventTests.cs index a107ee6ca..33dd1dfb5 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Events/WindowClosedEventTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Events/WindowClosedEventTests.cs @@ -2,6 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; namespace InfiniTests.InfiniFrame.Window.Events; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Events/WindowClosingRequestedEventTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Events/WindowClosingRequestedEventTests.cs similarity index 93% rename from tests/InfiniTests.InfiniFrame/Window/Events/WindowClosingRequestedEventTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Events/WindowClosingRequestedEventTests.cs index 20402b987..9aa7c6bde 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Events/WindowClosingRequestedEventTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Events/WindowClosingRequestedEventTests.cs @@ -2,6 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; namespace InfiniTests.InfiniFrame.Window.Events; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Events/WindowCreatedEventTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Events/WindowCreatedEventTests.cs similarity index 94% rename from tests/InfiniTests.InfiniFrame/Window/Events/WindowCreatedEventTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Events/WindowCreatedEventTests.cs index bf9ce0c58..9de7e80ab 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Events/WindowCreatedEventTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Events/WindowCreatedEventTests.cs @@ -2,6 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; namespace InfiniTests.InfiniFrame.Window.Events; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Events/WindowCreatingEventTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Events/WindowCreatingEventTests.cs similarity index 92% rename from tests/InfiniTests.InfiniFrame/Window/Events/WindowCreatingEventTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Events/WindowCreatingEventTests.cs index 41819fc4c..8733b1763 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Events/WindowCreatingEventTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Events/WindowCreatingEventTests.cs @@ -2,6 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; namespace InfiniTests.InfiniFrame.Window.Events; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Events/WindowFocusInEventTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Events/WindowFocusInEventTests.cs similarity index 94% rename from tests/InfiniTests.InfiniFrame/Window/Events/WindowFocusInEventTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Events/WindowFocusInEventTests.cs index 31499d9ff..4bb03e571 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Events/WindowFocusInEventTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Events/WindowFocusInEventTests.cs @@ -2,6 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; namespace InfiniTests.InfiniFrame.Window.Events; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Events/WindowFocusOutEventTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Events/WindowFocusOutEventTests.cs similarity index 95% rename from tests/InfiniTests.InfiniFrame/Window/Events/WindowFocusOutEventTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Events/WindowFocusOutEventTests.cs index 612504b30..ca6a21747 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Events/WindowFocusOutEventTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Events/WindowFocusOutEventTests.cs @@ -2,6 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; namespace InfiniTests.InfiniFrame.Window.Events; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Events/WindowLocationChangedEventTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Events/WindowLocationChangedEventTests.cs similarity index 94% rename from tests/InfiniTests.InfiniFrame/Window/Events/WindowLocationChangedEventTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Events/WindowLocationChangedEventTests.cs index 0147a9c51..b447b7737 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Events/WindowLocationChangedEventTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Events/WindowLocationChangedEventTests.cs @@ -2,6 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; namespace InfiniTests.InfiniFrame.Window.Events; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Events/WindowMaximizedEventTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Events/WindowMaximizedEventTests.cs similarity index 93% rename from tests/InfiniTests.InfiniFrame/Window/Events/WindowMaximizedEventTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Events/WindowMaximizedEventTests.cs index 7c15ebd9d..0607d682f 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Events/WindowMaximizedEventTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Events/WindowMaximizedEventTests.cs @@ -2,6 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; namespace InfiniTests.InfiniFrame.Window.Events; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Events/WindowMinimizedEventTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Events/WindowMinimizedEventTests.cs similarity index 93% rename from tests/InfiniTests.InfiniFrame/Window/Events/WindowMinimizedEventTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Events/WindowMinimizedEventTests.cs index 801319de9..608f8a059 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Events/WindowMinimizedEventTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Events/WindowMinimizedEventTests.cs @@ -2,6 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; namespace InfiniTests.InfiniFrame.Window.Events; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Events/WindowRestoredEventTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Events/WindowRestoredEventTests.cs similarity index 96% rename from tests/InfiniTests.InfiniFrame/Window/Events/WindowRestoredEventTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Events/WindowRestoredEventTests.cs index 4ea356796..78ffb0e00 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Events/WindowRestoredEventTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Events/WindowRestoredEventTests.cs @@ -2,6 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; namespace InfiniTests.InfiniFrame.Window.Events; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Events/WindowSizeChangedEventTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Events/WindowSizeChangedEventTests.cs similarity index 93% rename from tests/InfiniTests.InfiniFrame/Window/Events/WindowSizeChangedEventTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Events/WindowSizeChangedEventTests.cs index 7701f32c7..33e4509f9 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Events/WindowSizeChangedEventTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Events/WindowSizeChangedEventTests.cs @@ -2,6 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; namespace InfiniTests.InfiniFrame.Window.Events; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Browser/BrowserControlInitParametersTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/BrowserControlInitParametersTests.cs similarity index 87% rename from tests/InfiniTests.InfiniFrame/Window/Features/Browser/BrowserControlInitParametersTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/BrowserControlInitParametersTests.cs index 7edd3b921..f14850c5a 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Browser/BrowserControlInitParametersTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/BrowserControlInitParametersTests.cs @@ -2,7 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; namespace InfiniTests.InfiniFrame.Window.Features.Browser; // --------------------------------------------------------------------------------------------------------------------- @@ -18,7 +19,7 @@ public async Task AtBuilderStage_DirectAssignment(string value, CancellationToke // Act builder.Features.Browser.SetBrowserControlInitParameters(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Browser.BrowserControlInitParameters).IsEqualTo(value); @@ -34,7 +35,7 @@ public async Task AtBuilderStage_ExtensionAssignment(string value, CancellationT // Act IInfiniFrameWindowBuilder returnedBuilder = builder.SetBrowserControlInitParameters(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Browser.BrowserControlInitParameters).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Browser/BrowserPermissionsTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/BrowserPermissionsTests.cs similarity index 91% rename from tests/InfiniTests.InfiniFrame/Window/Features/Browser/BrowserPermissionsTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/BrowserPermissionsTests.cs index 08e37d8ba..50f703445 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Browser/BrowserPermissionsTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/BrowserPermissionsTests.cs @@ -2,7 +2,10 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Browser; // --------------------------------------------------------------------------------------------------------------------- @@ -19,7 +22,7 @@ public async Task AtBuilderStage_DirectAssignment(bool value, CancellationToken // Act builder.Features.Browser.EnableBrowserPermissions(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Browser.GrantBrowserPermissions).IsEqualTo(value); @@ -35,7 +38,7 @@ public async Task AtBuilderStage_ExtensionAssignment(bool value, CancellationTok // Act IInfiniFrameWindowBuilder returnedBuilder = builder.EnableBrowserPermissions(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Browser.GrantBrowserPermissions).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Browser/BrowserShortcutsTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/BrowserShortcutsTests.cs similarity index 91% rename from tests/InfiniTests.InfiniFrame/Window/Features/Browser/BrowserShortcutsTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/BrowserShortcutsTests.cs index 12c12039d..a8e7ed46a 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Browser/BrowserShortcutsTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/BrowserShortcutsTests.cs @@ -2,7 +2,10 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Browser; // --------------------------------------------------------------------------------------------------------------------- @@ -19,7 +22,7 @@ public async Task AtBuilderStage_DirectAssignment(bool value, CancellationToken // Act builder.Features.Browser.EnableBrowserShortcuts(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Browser.IsBrowserShortcutsEnabled).IsEqualTo(value); @@ -35,7 +38,7 @@ public async Task AtBuilderStage_ExtensionAssignment(bool value, CancellationTok // Act IInfiniFrameWindowBuilder returnedBuilder = builder.EnableBrowserShortcuts(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Browser.IsBrowserShortcutsEnabled).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Browser/ClearBrowserAutoFillTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/ClearBrowserAutoFillTests.cs similarity index 94% rename from tests/InfiniTests.InfiniFrame/Window/Features/Browser/ClearBrowserAutoFillTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/ClearBrowserAutoFillTests.cs index afcb071c9..25a263ebb 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Browser/ClearBrowserAutoFillTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/ClearBrowserAutoFillTests.cs @@ -2,6 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Browser; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Browser/ContextMenuTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/ContextMenuTests.cs similarity index 91% rename from tests/InfiniTests.InfiniFrame/Window/Features/Browser/ContextMenuTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/ContextMenuTests.cs index f6d964e2a..3f5b7c716 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Browser/ContextMenuTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/ContextMenuTests.cs @@ -2,7 +2,10 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Browser; // --------------------------------------------------------------------------------------------------------------------- @@ -19,7 +22,7 @@ public async Task AtBuilderStage_DirectAssignment(bool value, CancellationToken // Act builder.Features.Browser.EnableContextMenu(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Browser.IsContextMenuEnabled).IsEqualTo(value); @@ -35,7 +38,7 @@ public async Task AtBuilderStage_ExtensionAssignment(bool value, CancellationTok // Act IInfiniFrameWindowBuilder returnedBuilder = builder.EnableContextMenu(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Browser.IsContextMenuEnabled).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Browser/FileSystemAccessTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/FileSystemAccessTests.cs similarity index 91% rename from tests/InfiniTests.InfiniFrame/Window/Features/Browser/FileSystemAccessTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/FileSystemAccessTests.cs index 007461e50..1426c6636 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Browser/FileSystemAccessTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/FileSystemAccessTests.cs @@ -2,7 +2,10 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Browser; // --------------------------------------------------------------------------------------------------------------------- @@ -19,7 +22,7 @@ public async Task AtBuilderStage_DirectAssignment(bool value, CancellationToken // Act builder.Features.Browser.EnableFileSystemAccess(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Browser.IsFileSystemAccessEnabled).IsEqualTo(value); @@ -35,7 +38,7 @@ public async Task AtBuilderStage_ExtensionAssignment(bool value, CancellationTok // Act IInfiniFrameWindowBuilder returnedBuilder = builder.EnableFileSystemAccess(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Browser.IsFileSystemAccessEnabled).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Browser/IgnoreCertificateErrorsTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/IgnoreCertificateErrorsTests.cs similarity index 92% rename from tests/InfiniTests.InfiniFrame/Window/Features/Browser/IgnoreCertificateErrorsTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/IgnoreCertificateErrorsTests.cs index 0edfd89a1..709f2037a 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Browser/IgnoreCertificateErrorsTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/IgnoreCertificateErrorsTests.cs @@ -2,7 +2,10 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Browser; // --------------------------------------------------------------------------------------------------------------------- @@ -19,7 +22,7 @@ public async Task AtBuilderStage_DirectAssignment(bool value, CancellationToken // Act builder.Features.Browser.EnableIgnoreCertificateErrors(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Browser.IsIgnoreCertificateErrorsEnabled).IsEqualTo(value); @@ -35,7 +38,7 @@ public async Task AtBuilderStage_ExtensionAssignment(bool value, CancellationTok // Act IInfiniFrameWindowBuilder returnedBuilder = builder.EnableIgnoreCertificateErrors(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Browser.IsIgnoreCertificateErrorsEnabled).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Browser/JavascriptClipboardAccessTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/JavascriptClipboardAccessTests.cs similarity index 91% rename from tests/InfiniTests.InfiniFrame/Window/Features/Browser/JavascriptClipboardAccessTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/JavascriptClipboardAccessTests.cs index a7fc34aae..3d7fcc43b 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Browser/JavascriptClipboardAccessTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/JavascriptClipboardAccessTests.cs @@ -2,7 +2,10 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Browser; // --------------------------------------------------------------------------------------------------------------------- @@ -19,7 +22,7 @@ public async Task AtBuilderStage_DirectAssignment(bool value, CancellationToken // Act builder.Features.Browser.EnableJavascriptClipboardAccess(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Browser.IsJavascriptClipboardAccessEnabled).IsEqualTo(value); @@ -35,7 +38,7 @@ public async Task AtBuilderStage_ExtensionAssignment(bool value, CancellationTok // Act IInfiniFrameWindowBuilder returnedBuilder = builder.EnableJavascriptClipboardAccess(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Browser.IsJavascriptClipboardAccessEnabled).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Browser/MediaAutoPlayTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/MediaAutoPlayTests.cs similarity index 91% rename from tests/InfiniTests.InfiniFrame/Window/Features/Browser/MediaAutoPlayTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/MediaAutoPlayTests.cs index 32b77515c..eb86c2053 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Browser/MediaAutoPlayTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/MediaAutoPlayTests.cs @@ -2,7 +2,10 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Browser; // --------------------------------------------------------------------------------------------------------------------- @@ -19,7 +22,7 @@ public async Task AtBuilderStage_DirectAssignment(bool value, CancellationToken // Act builder.Features.Browser.EnableMediaAutoplay(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Browser.IsMediaAutoplayEnabled).IsEqualTo(value); @@ -35,7 +38,7 @@ public async Task AtBuilderStage_ExtensionAssignment(bool value, CancellationTok // Act IInfiniFrameWindowBuilder returnedBuilder = builder.EnableMediaAutoplay(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Browser.IsMediaAutoplayEnabled).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Browser/MediaStreamTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/MediaStreamTests.cs similarity index 91% rename from tests/InfiniTests.InfiniFrame/Window/Features/Browser/MediaStreamTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/MediaStreamTests.cs index 7c084559f..7037307c5 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Browser/MediaStreamTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/MediaStreamTests.cs @@ -2,7 +2,10 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Browser; // --------------------------------------------------------------------------------------------------------------------- @@ -19,7 +22,7 @@ public async Task AtBuilderStage_DirectAssignment(bool value, CancellationToken // Act builder.Features.Browser.EnableMediaStream(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Browser.IsMediaStreamEnabled).IsEqualTo(value); @@ -35,7 +38,7 @@ public async Task AtBuilderStage_ExtensionAssignment(bool value, CancellationTok // Act IInfiniFrameWindowBuilder returnedBuilder = builder.EnableMediaStream(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Browser.IsMediaStreamEnabled).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Browser/SmoothScrollingTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/SmoothScrollingTests.cs similarity index 91% rename from tests/InfiniTests.InfiniFrame/Window/Features/Browser/SmoothScrollingTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/SmoothScrollingTests.cs index 723c71db8..387963e2a 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Browser/SmoothScrollingTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/SmoothScrollingTests.cs @@ -2,7 +2,10 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Browser; // --------------------------------------------------------------------------------------------------------------------- @@ -19,7 +22,7 @@ public async Task AtBuilderStage_DirectAssignment(bool value, CancellationToken // Act builder.Features.Browser.EnableSmoothScrolling(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Browser.IsSmoothScrollingEnabled).IsEqualTo(value); @@ -35,7 +38,7 @@ public async Task AtBuilderStage_ExtensionAssignment(bool value, CancellationTok // Act IInfiniFrameWindowBuilder returnedBuilder = builder.EnableSmoothScrolling(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Browser.IsSmoothScrollingEnabled).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Browser/StatusBarTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/StatusBarTests.cs similarity index 91% rename from tests/InfiniTests.InfiniFrame/Window/Features/Browser/StatusBarTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/StatusBarTests.cs index 5cff71e0b..a52dcaf38 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Browser/StatusBarTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/StatusBarTests.cs @@ -2,7 +2,10 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Browser; // --------------------------------------------------------------------------------------------------------------------- @@ -19,7 +22,7 @@ public async Task AtBuilderStage_DirectAssignment(bool value, CancellationToken // Act builder.Features.Browser.EnableStatusBar(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Browser.IsStatusBarEnabled).IsEqualTo(value); @@ -35,7 +38,7 @@ public async Task AtBuilderStage_ExtensionAssignment(bool value, CancellationTok // Act IInfiniFrameWindowBuilder returnedBuilder = builder.EnableStatusBar(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Browser.IsStatusBarEnabled).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Browser/TemporaryFilesPathTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/TemporaryFilesPathTests.cs similarity index 87% rename from tests/InfiniTests.InfiniFrame/Window/Features/Browser/TemporaryFilesPathTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/TemporaryFilesPathTests.cs index 7d52bb2dc..973d044bd 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Browser/TemporaryFilesPathTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/TemporaryFilesPathTests.cs @@ -2,7 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; namespace InfiniTests.InfiniFrame.Window.Features.Browser; // --------------------------------------------------------------------------------------------------------------------- @@ -16,7 +17,7 @@ public async Task AtBuilderStage_DefaultValueIsAppliedToNativeParameters(Cancell var builder = InfiniFrameWindowBuilder.Create(); // Act - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(initParameters.TemporaryFilesPath).IsEqualTo(builder.Features.Browser.TemporaryFilesPath); @@ -33,7 +34,7 @@ public async Task AtBuilderStage_ExtensionAssignmentIsAppliedToNativeParameters( // Act IInfiniFrameWindowBuilder returnedBuilder = builder.SetTemporaryFilesPath(inputPath); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Browser.TemporaryFilesPath).IsEqualTo(expectedPath); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Browser/UserAgentTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/UserAgentTests.cs similarity index 92% rename from tests/InfiniTests.InfiniFrame/Window/Features/Browser/UserAgentTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/UserAgentTests.cs index a2f7d0c4e..6c9dd5b5d 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Browser/UserAgentTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/UserAgentTests.cs @@ -2,7 +2,10 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Browser; // --------------------------------------------------------------------------------------------------------------------- @@ -21,7 +24,7 @@ public async Task AtBuilderStage_DirectAssignment(string? value, string? expecte // Act builder.Features.Browser.SetUserAgent(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Browser.UserAgent).IsEqualTo(expected); @@ -39,7 +42,7 @@ public async Task AtBuilderStage_ExtensionAssignment(string? value, string? expe // Act IInfiniFrameWindowBuilder returnedBuilder = builder.SetUserAgent(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(returnedBuilder).IsSameReferenceAs(builder); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Browser/WebSecurityTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/WebSecurityTests.cs similarity index 91% rename from tests/InfiniTests.InfiniFrame/Window/Features/Browser/WebSecurityTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/WebSecurityTests.cs index e2dc9c1ec..fe2acf609 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Browser/WebSecurityTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Browser/WebSecurityTests.cs @@ -2,7 +2,10 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Browser; // --------------------------------------------------------------------------------------------------------------------- @@ -19,7 +22,7 @@ public async Task AtBuilderStage_DirectAssignment(bool value, CancellationToken // Act builder.Features.Browser.EnableWebSecurity(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Browser.IsWebSecurityEnabled).IsEqualTo(value); @@ -35,7 +38,7 @@ public async Task AtBuilderStage_ExtensionAssignment(bool value, CancellationTok // Act IInfiniFrameWindowBuilder returnedBuilder = builder.EnableWebSecurity(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Browser.IsWebSecurityEnabled).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Debugging/DebugEndpointResultTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Debugging/DebugEndpointResultTests.cs similarity index 99% rename from tests/InfiniTests.InfiniFrame/Window/Features/Debugging/DebugEndpointResultTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Debugging/DebugEndpointResultTests.cs index dc5fc0271..da353f257 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Debugging/DebugEndpointResultTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Debugging/DebugEndpointResultTests.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame; +using InfiniFrame.Window.Features.Debugging; namespace InfiniTests.InfiniFrame.Window.Features.Debugging; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Debugging/DebuggingStartupParametersTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Debugging/DebuggingStartupParametersTests.cs similarity index 91% rename from tests/InfiniTests.InfiniFrame/Window/Features/Debugging/DebuggingStartupParametersTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Debugging/DebuggingStartupParametersTests.cs index facad89fc..3c1775046 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Debugging/DebuggingStartupParametersTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Debugging/DebuggingStartupParametersTests.cs @@ -1,8 +1,8 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; namespace InfiniTests.InfiniFrame.Window.Features.Debugging; // --------------------------------------------------------------------------------------------------------------------- @@ -38,7 +38,7 @@ CancellationToken ct builder.Debugging.SetRemoteDebuggingPort(remoteDebuggingPort); } - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(initParameters.DevToolsEnabled).IsEqualTo(devToolsEnabled); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Debugging/DevToolsTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Debugging/DevToolsTests.cs similarity index 91% rename from tests/InfiniTests.InfiniFrame/Window/Features/Debugging/DevToolsTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Debugging/DevToolsTests.cs index 36a1f22df..6b3f0a900 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Debugging/DevToolsTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Debugging/DevToolsTests.cs @@ -2,7 +2,10 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Debugging; // --------------------------------------------------------------------------------------------------------------------- @@ -19,7 +22,7 @@ public async Task AtBuilderStage_DirectAssignment(bool value, CancellationToken // Act builder.Features.Debugging.EnableDevTools(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Debugging.IsDevToolsEnabled).IsEqualTo(value); @@ -35,7 +38,7 @@ public async Task AtBuilderStage_ExtensionAssignment(bool value, CancellationTok // Act IInfiniFrameWindowBuilder returnedBuilder = builder.EnableDevTools(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Debugging.IsDevToolsEnabled).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Debugging/GetDebugDiagnosticsTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Debugging/GetDebugDiagnosticsTests.cs similarity index 97% rename from tests/InfiniTests.InfiniFrame/Window/Features/Debugging/GetDebugDiagnosticsTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Debugging/GetDebugDiagnosticsTests.cs index 9e6dde093..c489a6a26 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Debugging/GetDebugDiagnosticsTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Debugging/GetDebugDiagnosticsTests.cs @@ -3,6 +3,9 @@ // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; using InfiniFrame.Debugging; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; namespace InfiniTests.InfiniFrame.Window.Features.Debugging; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Debugging/RemoteDebuggingEndpointReadinessTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Debugging/RemoteDebuggingEndpointReadinessTests.cs similarity index 97% rename from tests/InfiniTests.InfiniFrame/Window/Features/Debugging/RemoteDebuggingEndpointReadinessTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Debugging/RemoteDebuggingEndpointReadinessTests.cs index dab490368..f18c78e8b 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Debugging/RemoteDebuggingEndpointReadinessTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Debugging/RemoteDebuggingEndpointReadinessTests.cs @@ -4,6 +4,9 @@ using System.Net; using System.Net.Sockets; using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; namespace InfiniTests.InfiniFrame.Window.Features.Debugging; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Debugging/RemoteDebuggingPortCollisionTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Debugging/RemoteDebuggingPortCollisionTests.cs similarity index 94% rename from tests/InfiniTests.InfiniFrame/Window/Features/Debugging/RemoteDebuggingPortCollisionTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Debugging/RemoteDebuggingPortCollisionTests.cs index 44e72fb68..ce13179d9 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Debugging/RemoteDebuggingPortCollisionTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Debugging/RemoteDebuggingPortCollisionTests.cs @@ -3,6 +3,9 @@ // --------------------------------------------------------------------------------------------------------------------- using System.Net; using System.Net.Sockets; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; namespace InfiniTests.InfiniFrame.Window.Features.Debugging; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Debugging/RemoteDebuggingPortTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Debugging/RemoteDebuggingPortTests.cs similarity index 92% rename from tests/InfiniTests.InfiniFrame/Window/Features/Debugging/RemoteDebuggingPortTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Debugging/RemoteDebuggingPortTests.cs index 511f6f884..51557646a 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Debugging/RemoteDebuggingPortTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Debugging/RemoteDebuggingPortTests.cs @@ -2,7 +2,11 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; namespace InfiniTests.InfiniFrame.Window.Features.Debugging; // --------------------------------------------------------------------------------------------------------------------- @@ -32,7 +36,7 @@ public async Task AtBuilderStage_DirectAssignment(int value, CancellationToken c // Act builder.Features.Debugging.SetRemoteDebuggingPort(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Debugging.RemoteDebuggingPort).IsEqualTo(value); @@ -53,7 +57,7 @@ public async Task AtBuilderStage_ExtensionAssignment(int value, CancellationToke // Act IInfiniFrameWindowBuilder returnedBuilder = builder.SetRemoteDebuggingPort(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(returnedBuilder).IsSameReferenceAs(builder); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Debugging/SupportsRemoteDebuggingEndpointTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Debugging/SupportsRemoteDebuggingEndpointTests.cs similarity index 96% rename from tests/InfiniTests.InfiniFrame/Window/Features/Debugging/SupportsRemoteDebuggingEndpointTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Debugging/SupportsRemoteDebuggingEndpointTests.cs index a511a5048..6987c4dc5 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Debugging/SupportsRemoteDebuggingEndpointTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Debugging/SupportsRemoteDebuggingEndpointTests.cs @@ -2,6 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Debugging; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Debugging/SupportsWebInspectorAttachTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Debugging/SupportsWebInspectorAttachTests.cs similarity index 96% rename from tests/InfiniTests.InfiniFrame/Window/Features/Debugging/SupportsWebInspectorAttachTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Debugging/SupportsWebInspectorAttachTests.cs index 3b9e991ff..7e3897875 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Debugging/SupportsWebInspectorAttachTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Debugging/SupportsWebInspectorAttachTests.cs @@ -2,6 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Debugging; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Debugging/TryGetRemoteDebuggingEndpointTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Debugging/TryGetRemoteDebuggingEndpointTests.cs similarity index 97% rename from tests/InfiniTests.InfiniFrame/Window/Features/Debugging/TryGetRemoteDebuggingEndpointTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Debugging/TryGetRemoteDebuggingEndpointTests.cs index debddf92a..7b71ac545 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Debugging/TryGetRemoteDebuggingEndpointTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Debugging/TryGetRemoteDebuggingEndpointTests.cs @@ -2,6 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; namespace InfiniTests.InfiniFrame.Window.Features.Debugging; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Debugging/TryProbeRemoteDebuggingEndpointTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Debugging/TryProbeRemoteDebuggingEndpointTests.cs similarity index 97% rename from tests/InfiniTests.InfiniFrame/Window/Features/Debugging/TryProbeRemoteDebuggingEndpointTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Debugging/TryProbeRemoteDebuggingEndpointTests.cs index 519290eec..38157f874 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Debugging/TryProbeRemoteDebuggingEndpointTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Debugging/TryProbeRemoteDebuggingEndpointTests.cs @@ -2,6 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; namespace InfiniTests.InfiniFrame.Window.Features.Debugging; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Debugging/WebInspectorTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Debugging/WebInspectorTests.cs similarity index 89% rename from tests/InfiniTests.InfiniFrame/Window/Features/Debugging/WebInspectorTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Debugging/WebInspectorTests.cs index 2e6608070..5a46a4e8d 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Debugging/WebInspectorTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Debugging/WebInspectorTests.cs @@ -2,7 +2,10 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Debugging; // --------------------------------------------------------------------------------------------------------------------- @@ -25,7 +28,7 @@ public async Task AtBuilderStage_DirectAssignment(bool value, CancellationToken // Act builder.Features.Debugging.EnableWebInspector(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Debugging.IsWebInspectorEnabled).IsEqualTo(value); @@ -46,7 +49,7 @@ public async Task AtBuilderStage_DirectAssignment_UnhappyFlow(bool value, Cancel builder.Features.Debugging.EnableWebInspector(value); #pragma warning restore CA1416 }); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Debugging.IsWebInspectorEnabled).IsFalse(); @@ -68,7 +71,7 @@ public async Task AtBuilderStage_ExtensionAssignment(bool value, CancellationTok // Act IInfiniFrameWindowBuilder returnedBuilder = builder.EnableWebInspector(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Debugging.IsWebInspectorEnabled).IsEqualTo(value); @@ -90,7 +93,7 @@ public async Task AtBuilderStage_ExtensionAssignment_UnhappyFlow(bool value, Can builder.EnableWebInspector(value); #pragma warning restore CA1416 }); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Debugging.IsWebInspectorEnabled).IsFalse(); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Decorations/BackgroundColorTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Decorations/BackgroundColorTests.cs similarity index 98% rename from tests/InfiniTests.InfiniFrame/Window/Features/Decorations/BackgroundColorTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Decorations/BackgroundColorTests.cs index e5431073b..e0eab5d9d 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Decorations/BackgroundColorTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Decorations/BackgroundColorTests.cs @@ -3,6 +3,9 @@ // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; using InfiniFrame.Utilities; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Decorations; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Decorations/ChromelessTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Decorations/ChromelessTests.cs similarity index 88% rename from tests/InfiniTests.InfiniFrame/Window/Features/Decorations/ChromelessTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Decorations/ChromelessTests.cs index add2bfa20..fbc6e23e9 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Decorations/ChromelessTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Decorations/ChromelessTests.cs @@ -2,7 +2,10 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Decorations; // --------------------------------------------------------------------------------------------------------------------- @@ -18,7 +21,7 @@ public async Task AtBuilderStage_DirectAssignment(bool value, CancellationToken // Act builder.Features.Decorations.SetChromeless(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Decorations.IsChromeless).IsEqualTo(value); @@ -34,7 +37,7 @@ public async Task AtBuilderStage_ExtensionAssignment(bool value, CancellationTok // Act IInfiniFrameWindowBuilder returnedBuilder = builder.SetChromeless(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Decorations.IsChromeless).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Decorations/IconFileTaskbarTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Decorations/IconFileTaskbarTests.cs similarity index 96% rename from tests/InfiniTests.InfiniFrame/Window/Features/Decorations/IconFileTaskbarTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Decorations/IconFileTaskbarTests.cs index cb0953378..d41aafa9b 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Decorations/IconFileTaskbarTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Decorations/IconFileTaskbarTests.cs @@ -2,7 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniTests.Native; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using WindowsNative = InfiniTests.TestSupport.Native.WindowsNative; namespace InfiniTests.InfiniFrame.Window.Features.Decorations; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Decorations/IconFileTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Decorations/IconFileTests.cs similarity index 92% rename from tests/InfiniTests.InfiniFrame/Window/Features/Decorations/IconFileTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Decorations/IconFileTests.cs index a05024b47..7c5b22d09 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Decorations/IconFileTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Decorations/IconFileTests.cs @@ -2,7 +2,10 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Decorations; // --------------------------------------------------------------------------------------------------------------------- @@ -16,7 +19,7 @@ public async Task AtBuilderStage_DirectAssignment_ResolvesIconForNativeParameter string value = Path.Join(Path.GetTempPath(), $"{Guid.NewGuid():N}.ico"); // Act - InfiniFrameNativeParameters initParameters; + InfiniFrameNativeWindowParameters initParameters; try { await File.WriteAllTextAsync(value, "icon", ct); builder.Features.Decorations.SetIconFile(value); @@ -39,7 +42,7 @@ public async Task AtBuilderStage_ExtensionAssignment_InvalidPath_DoesNotPassIcon // Act IInfiniFrameWindowBuilder returnedBuilder = builder.SetIconFile(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Decorations.IconFilePath).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Decorations/LimitLinuxWindowTitleLengthTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Decorations/LimitLinuxWindowTitleLengthTests.cs similarity index 95% rename from tests/InfiniTests.InfiniFrame/Window/Features/Decorations/LimitLinuxWindowTitleLengthTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Decorations/LimitLinuxWindowTitleLengthTests.cs index 5fc192f75..7ded53adb 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Decorations/LimitLinuxWindowTitleLengthTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Decorations/LimitLinuxWindowTitleLengthTests.cs @@ -2,6 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Decorations; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Decorations/TitleTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Decorations/TitleTests.cs similarity index 87% rename from tests/InfiniTests.InfiniFrame/Window/Features/Decorations/TitleTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Decorations/TitleTests.cs index a58291b42..451d352ce 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Decorations/TitleTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Decorations/TitleTests.cs @@ -2,7 +2,10 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Decorations; // --------------------------------------------------------------------------------------------------------------------- @@ -18,7 +21,7 @@ public async Task AtBuilderStage_DirectAssignment(string value, CancellationToke // Act builder.Features.Decorations.SetTitle(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Decorations.Title).IsEqualTo(value); @@ -34,7 +37,7 @@ public async Task AtBuilderStage_ExtensionAssignment(string value, CancellationT // Act IInfiniFrameWindowBuilder returnedBuilder = builder.SetTitle(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Decorations.Title).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Decorations/TransparentTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Decorations/TransparentTests.cs similarity index 89% rename from tests/InfiniTests.InfiniFrame/Window/Features/Decorations/TransparentTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Decorations/TransparentTests.cs index 2bfb090fc..bdbc8efdc 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Decorations/TransparentTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Decorations/TransparentTests.cs @@ -2,7 +2,10 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Decorations; // --------------------------------------------------------------------------------------------------------------------- @@ -18,7 +21,7 @@ public async Task AtBuilderStage_DirectAssignment(bool value, CancellationToken // Act builder.Features.Decorations.SetTransparent(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Decorations.IsTransparent).IsEqualTo(value); @@ -34,7 +37,7 @@ public async Task AtBuilderStage_ExtensionAssignment(bool value, CancellationTok // Act IInfiniFrameWindowBuilder returnedBuilder = builder.SetTransparent(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Decorations.IsTransparent).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/FilePickerDialogs/FilePickerDialogsDefaultFileNameTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/FilePickerDialogs/FilePickerDialogsDefaultFileNameTests.cs similarity index 96% rename from tests/InfiniTests.InfiniFrame/Window/Features/FilePickerDialogs/FilePickerDialogsDefaultFileNameTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/FilePickerDialogs/FilePickerDialogsDefaultFileNameTests.cs index 13f95f46e..0a7bc7c27 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/FilePickerDialogs/FilePickerDialogsDefaultFileNameTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/FilePickerDialogs/FilePickerDialogsDefaultFileNameTests.cs @@ -2,6 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.FilePickerDialogs; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/FilePickerDialogs/FilePickerDialogsWhenClosedAsyncTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/FilePickerDialogs/FilePickerDialogsWhenClosedAsyncTests.cs similarity index 98% rename from tests/InfiniTests.InfiniFrame/Window/Features/FilePickerDialogs/FilePickerDialogsWhenClosedAsyncTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/FilePickerDialogs/FilePickerDialogsWhenClosedAsyncTests.cs index 371f6e727..6df50db6d 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/FilePickerDialogs/FilePickerDialogsWhenClosedAsyncTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/FilePickerDialogs/FilePickerDialogsWhenClosedAsyncTests.cs @@ -3,6 +3,8 @@ // --------------------------------------------------------------------------------------------------------------------- using System.Diagnostics.CodeAnalysis; using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.FilePickerDialogs; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/FilePickerDialogs/FilePickerDialogsWhenClosedSyncTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/FilePickerDialogs/FilePickerDialogsWhenClosedSyncTests.cs similarity index 97% rename from tests/InfiniTests.InfiniFrame/Window/Features/FilePickerDialogs/FilePickerDialogsWhenClosedSyncTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/FilePickerDialogs/FilePickerDialogsWhenClosedSyncTests.cs index 4a28ba8f9..1ab7dc0f8 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/FilePickerDialogs/FilePickerDialogsWhenClosedSyncTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/FilePickerDialogs/FilePickerDialogsWhenClosedSyncTests.cs @@ -2,6 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.FilePickerDialogs; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/FilePickerDialogs/WindowFeatureFilePickerFilterTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/FilePickerDialogs/WindowFeatureFilePickerFilterTests.cs similarity index 98% rename from tests/InfiniTests.InfiniFrame/Window/Features/FilePickerDialogs/WindowFeatureFilePickerFilterTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/FilePickerDialogs/WindowFeatureFilePickerFilterTests.cs index 68d54f3c9..857fbcb16 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/FilePickerDialogs/WindowFeatureFilePickerFilterTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/FilePickerDialogs/WindowFeatureFilePickerFilterTests.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame; +using InfiniFrame.Window.Features.FilePickerDialogs; namespace InfiniTests.InfiniFrame.Window.Features.FilePickerDialogs; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/InstanceArbitration/InstanceArbitrationBuilderFeatureTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/InstanceArbitration/InstanceArbitrationBuilderFeatureTests.cs similarity index 98% rename from tests/InfiniTests.InfiniFrame/Window/Features/InstanceArbitration/InstanceArbitrationBuilderFeatureTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/InstanceArbitration/InstanceArbitrationBuilderFeatureTests.cs index 8d49ee6f8..15d3085b3 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/InstanceArbitration/InstanceArbitrationBuilderFeatureTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/InstanceArbitration/InstanceArbitrationBuilderFeatureTests.cs @@ -2,6 +2,7 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniFrame.Window.Builder; namespace InfiniTests.InfiniFrame.Window.Features.InstanceArbitration; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/InstanceArbitration/InstanceArbitrationModeTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/InstanceArbitration/InstanceArbitrationModeTests.cs similarity index 100% rename from tests/InfiniTests.InfiniFrame/Window/Features/InstanceArbitration/InstanceArbitrationModeTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/InstanceArbitration/InstanceArbitrationModeTests.cs diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/InstanceArbitration/InstanceArbitrationTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/InstanceArbitration/InstanceArbitrationTests.cs similarity index 72% rename from tests/InfiniTests.InfiniFrame/Window/Features/InstanceArbitration/InstanceArbitrationTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/InstanceArbitration/InstanceArbitrationTests.cs index f417e9aac..a4611b3ce 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/InstanceArbitration/InstanceArbitrationTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/InstanceArbitration/InstanceArbitrationTests.cs @@ -1,6 +1,8 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- +using InfiniTests.TestSupport.Attributes; + namespace InfiniTests.InfiniFrame.Window.Features.InstanceArbitration; // --------------------------------------------------------------------------------------------------------------------- // Code @@ -14,7 +16,7 @@ public async Task TryAcquirePrimaryInstance_FirstCall_ReturnsTrue(CancellationTo string mutexName = $"InfiniFrame.Test.{Guid.NewGuid():N}"; // Act - bool result = global::InfiniFrame.InstanceArbitration.TryAcquirePrimaryInstance(mutexName); + bool result = global::InfiniFrame.Window.Features.InstanceArbitration.InstanceArbitration.TryAcquirePrimaryInstance(mutexName); // Assert await Assert.That(result).IsTrue(); @@ -27,8 +29,8 @@ public async Task TryAcquirePrimaryInstance_SecondCall_ReturnsFalse(Cancellation string mutexName = $"InfiniFrame.Test.{Guid.NewGuid():N}"; // Act - bool first = global::InfiniFrame.InstanceArbitration.TryAcquirePrimaryInstance(mutexName); - bool second = global::InfiniFrame.InstanceArbitration.TryAcquirePrimaryInstance(mutexName); + bool first = global::InfiniFrame.Window.Features.InstanceArbitration.InstanceArbitration.TryAcquirePrimaryInstance(mutexName); + bool second = global::InfiniFrame.Window.Features.InstanceArbitration.InstanceArbitration.TryAcquirePrimaryInstance(mutexName); // Assert await Assert.That(first).IsTrue(); @@ -39,10 +41,10 @@ public async Task TryAcquirePrimaryInstance_SecondCall_ReturnsFalse(Cancellation [NotInParallelInfiniTests] public async Task TryAcquirePrimaryInstance_NullMutexName_UsesDefault(CancellationToken ct) { // Arrange - acquire the default mutex (or discover another process already holds it) - bool first = global::InfiniFrame.InstanceArbitration.TryAcquirePrimaryInstance(null); + bool first = global::InfiniFrame.Window.Features.InstanceArbitration.InstanceArbitration.TryAcquirePrimaryInstance(null); // Act - call again with null; same default name means this must fail within the same process - bool second = global::InfiniFrame.InstanceArbitration.TryAcquirePrimaryInstance(null); + bool second = global::InfiniFrame.Window.Features.InstanceArbitration.InstanceArbitration.TryAcquirePrimaryInstance(null); // Assert - verify the method uses a consistent name (not a new GUID each time) // If the first call succeeded, the second must fail (same mutex name). @@ -53,7 +55,7 @@ public async Task TryAcquirePrimaryInstance_NullMutexName_UsesDefault(Cancellati [Test] public async Task IsProcessElevated_ReturnsTrueOrFalse(CancellationToken ct) { // Act - bool result = global::InfiniFrame.InstanceArbitration.IsProcessElevated(); + bool result = global::InfiniFrame.Window.Features.InstanceArbitration.InstanceArbitration.IsProcessElevated(); // Assert - verify it returns a valid bool without throwing await Assert.That(result || !result).IsTrue(); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/InstanceArbitration/InstanceArbitrationWindowStageTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/InstanceArbitration/InstanceArbitrationWindowStageTests.cs similarity index 89% rename from tests/InfiniTests.InfiniFrame/Window/Features/InstanceArbitration/InstanceArbitrationWindowStageTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/InstanceArbitration/InstanceArbitrationWindowStageTests.cs index 34ba71330..af5b36543 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/InstanceArbitration/InstanceArbitrationWindowStageTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/InstanceArbitration/InstanceArbitrationWindowStageTests.cs @@ -2,6 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.InstanceArbitration; // --------------------------------------------------------------------------------------------------------------------- @@ -26,7 +28,7 @@ public async Task AtWindowStage_BuildWithArbitration_Succeeds(CancellationToken public async Task AtWindowStage_BuildWithArbitration_SecondInstance_Throws(CancellationToken ct) { // Arrange string mutexName = $"InfiniFrame.Test.{Guid.NewGuid():N}"; - global::InfiniFrame.InstanceArbitration.TryAcquirePrimaryInstance(mutexName); + global::InfiniFrame.Window.Features.InstanceArbitration.InstanceArbitration.TryAcquirePrimaryInstance(mutexName); // Act & Assert await Assert.That(() => { diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Invoke/InvokeTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Invoke/InvokeTests.cs similarity index 98% rename from tests/InfiniTests.InfiniFrame/Window/Features/Invoke/InvokeTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Invoke/InvokeTests.cs index 19942a429..025ae8f05 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Invoke/InvokeTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Invoke/InvokeTests.cs @@ -2,6 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Invoke; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/JavaScript/ExecuteJavaScriptTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/JavaScript/ExecuteJavaScriptTests.cs similarity index 94% rename from tests/InfiniTests.InfiniFrame/Window/Features/JavaScript/ExecuteJavaScriptTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/JavaScript/ExecuteJavaScriptTests.cs index b99dd44e9..82603198d 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/JavaScript/ExecuteJavaScriptTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/JavaScript/ExecuteJavaScriptTests.cs @@ -2,10 +2,12 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using System.Diagnostics.CodeAnalysis; -using InfiniFrame; -using InfiniFrame.Interop; -using InfiniFrame.NativeBridge.Parameters; -using InfiniTests.Substitutes; +using InfiniFrame.Js.Interop; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniFrame.Window.Events; +using InfiniFrame.Window.Features.WebMessaging.Handlers; +using InfiniTests.TestSupport.Substitutes; using Microsoft.Extensions.Logging.Abstractions; namespace InfiniTests.InfiniFrame.Window.Features.JavaScript; @@ -119,7 +121,7 @@ private static (InfiniFrameWindowBuilder Builder, InfiniFrameEvents Events, Reco .BindToBuilder(builder); var events = new InfiniFrameEvents(eventsStore, NullLogger.Instance); - var nativeParameters = default(InfiniFrameNativeParameters); + var nativeParameters = default(InfiniFrameNativeWindowParameters); events.AssignToNativeParameters(ref nativeParameters); events.AssignToWindow(window.Window); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Lifecycle/CleanupNativeHandleTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Lifecycle/CleanupNativeHandleTests.cs similarity index 89% rename from tests/InfiniTests.InfiniFrame/Window/Features/Lifecycle/CleanupNativeHandleTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Lifecycle/CleanupNativeHandleTests.cs index b07497bcc..554455cf6 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Lifecycle/CleanupNativeHandleTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Lifecycle/CleanupNativeHandleTests.cs @@ -5,7 +5,11 @@ using System.Reflection; using FluentValidation; using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Events; +using InfiniFrame.Window.Features.Lifecycle; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; using Microsoft.Extensions.Logging.Abstractions; namespace InfiniTests.InfiniFrame.Window.Features.Lifecycle; @@ -24,7 +28,7 @@ public async Task CleanupNativeHandle_ReleasesEventNativeCallbackRoot(Cancellati window.Events.Returns(events); window.LifecycleState.Returns(InfiniFrameWindowLifecycleState.TeardownComplete); - Mock> validator = MockFactory.CreateValidatorMock(); + Mock> validator = MockFactory.CreateValidatorMock(); var lifecycle = new LifecycleInfiniFrameWindowFeature( window.Object, NullLogger.Instance, diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Lifecycle/CloseAsyncTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Lifecycle/CloseAsyncTests.cs similarity index 96% rename from tests/InfiniTests.InfiniFrame/Window/Features/Lifecycle/CloseAsyncTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Lifecycle/CloseAsyncTests.cs index 1d5dc9c00..5117d911d 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Lifecycle/CloseAsyncTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Lifecycle/CloseAsyncTests.cs @@ -2,6 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Lifecycle; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Lifecycle/CloseDuringWebViewInitializationTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Lifecycle/CloseDuringWebViewInitializationTests.cs similarity index 94% rename from tests/InfiniTests.InfiniFrame/Window/Features/Lifecycle/CloseDuringWebViewInitializationTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Lifecycle/CloseDuringWebViewInitializationTests.cs index 582c0b547..db1030323 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Lifecycle/CloseDuringWebViewInitializationTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Lifecycle/CloseDuringWebViewInitializationTests.cs @@ -2,6 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Lifecycle; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Lifecycle/CloseSyncTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Lifecycle/CloseSyncTests.cs similarity index 96% rename from tests/InfiniTests.InfiniFrame/Window/Features/Lifecycle/CloseSyncTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Lifecycle/CloseSyncTests.cs index f17c4ff0b..a4312190f 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Lifecycle/CloseSyncTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Lifecycle/CloseSyncTests.cs @@ -2,6 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Lifecycle; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Lifecycle/CrossThreadWindowLifecycleTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Lifecycle/CrossThreadWindowLifecycleTests.cs similarity index 97% rename from tests/InfiniTests.InfiniFrame/Window/Features/Lifecycle/CrossThreadWindowLifecycleTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Lifecycle/CrossThreadWindowLifecycleTests.cs index e3d391316..933307764 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Lifecycle/CrossThreadWindowLifecycleTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Lifecycle/CrossThreadWindowLifecycleTests.cs @@ -2,6 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Lifecycle; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Lifecycle/DisposeAsyncTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Lifecycle/DisposeAsyncTests.cs similarity index 97% rename from tests/InfiniTests.InfiniFrame/Window/Features/Lifecycle/DisposeAsyncTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Lifecycle/DisposeAsyncTests.cs index 234e3d291..5c50f3433 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Lifecycle/DisposeAsyncTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Lifecycle/DisposeAsyncTests.cs @@ -3,6 +3,8 @@ // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; using InfiniFrame.NativeBridge.Dialogs; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Lifecycle; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Lifecycle/IsClosedOrClosingTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Lifecycle/IsClosedOrClosingTests.cs similarity index 96% rename from tests/InfiniTests.InfiniFrame/Window/Features/Lifecycle/IsClosedOrClosingTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Lifecycle/IsClosedOrClosingTests.cs index 4616fc279..741bc610f 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Lifecycle/IsClosedOrClosingTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Lifecycle/IsClosedOrClosingTests.cs @@ -2,6 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Lifecycle; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Lifecycle/NativeLifetimeStressTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Lifecycle/NativeLifetimeStressTests.cs similarity index 97% rename from tests/InfiniTests.InfiniFrame/Window/Features/Lifecycle/NativeLifetimeStressTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Lifecycle/NativeLifetimeStressTests.cs index 74fd4b6d8..f28474b7d 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Lifecycle/NativeLifetimeStressTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Lifecycle/NativeLifetimeStressTests.cs @@ -3,6 +3,8 @@ // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; using InfiniFrame.NativeBridge; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Lifecycle; // --------------------------------------------------------------------------------------------------------------------- @@ -126,7 +128,7 @@ .. Enumerable.Range(0, ConcurrentFeatureCallerCount) catch (OperationCanceledException) when (stop.IsCancellationRequested) {} // Assert - await Assert.That(completedCalls).IsGreaterThanOrEqualTo(0); + await Assert.That(completedCalls).IsGreaterThan(0); await Assert.That((int)window.Features.Lifecycle.State) .IsGreaterThanOrEqualTo((int)InfiniFrameWindowLifecycleState.TeardownPending); await Assert.That(() => window.Features.State.IsFocused).Throws(); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Lifecycle/WaitForCloseAsyncTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Lifecycle/WaitForCloseAsyncTests.cs similarity index 97% rename from tests/InfiniTests.InfiniFrame/Window/Features/Lifecycle/WaitForCloseAsyncTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Lifecycle/WaitForCloseAsyncTests.cs index f1855dfb0..e7f7d3e5a 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Lifecycle/WaitForCloseAsyncTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Lifecycle/WaitForCloseAsyncTests.cs @@ -2,6 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Lifecycle; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Lifecycle/WaitForCloseSyncTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Lifecycle/WaitForCloseSyncTests.cs similarity index 96% rename from tests/InfiniTests.InfiniFrame/Window/Features/Lifecycle/WaitForCloseSyncTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Lifecycle/WaitForCloseSyncTests.cs index 55d8e1ee1..3cb827b02 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Lifecycle/WaitForCloseSyncTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Lifecycle/WaitForCloseSyncTests.cs @@ -2,6 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Lifecycle; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Menu/MenuBarSerializationTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Menu/MenuBarSerializationTests.cs similarity index 100% rename from tests/InfiniTests.InfiniFrame/Window/Features/Menu/MenuBarSerializationTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Menu/MenuBarSerializationTests.cs diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Menu/MenuBarTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Menu/MenuBarTests.cs similarity index 89% rename from tests/InfiniTests.InfiniFrame/Window/Features/Menu/MenuBarTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Menu/MenuBarTests.cs index 51a1b170e..5c3c9b05e 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Menu/MenuBarTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Menu/MenuBarTests.cs @@ -3,7 +3,8 @@ // --------------------------------------------------------------------------------------------------------------------- using System.Text.Json; using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; namespace InfiniTests.InfiniFrame.Window.Features.Menu; // --------------------------------------------------------------------------------------------------------------------- @@ -29,7 +30,7 @@ public async Task AtBuilderStage_DirectAssignment(CancellationToken ct) { // Act builder.Features.Menu.SetMenuBar(menuBar); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Menu.MenuBar).IsEqualTo(menuBar); @@ -48,7 +49,7 @@ public async Task AtBuilderStage_ExtensionAssignment(CancellationToken ct) { // Act IInfiniFrameWindowBuilder returnedBuilder = builder.SetMenuBar(menuBar); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Menu.MenuBar).IsEqualTo(menuBar); @@ -62,7 +63,7 @@ public async Task AtBuilderStage_DefaultIsEmpty(CancellationToken ct) { var builder = InfiniFrameWindowBuilder.Create(); // Act - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Menu.MenuBar.Items.IsEmpty).IsTrue(); @@ -76,7 +77,7 @@ public async Task AtBuilderStage_NullMenuBar(CancellationToken ct) { // Act builder.Features.Menu.SetMenuBar(null!); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Menu.MenuBar.Items.IsEmpty).IsTrue(); @@ -99,7 +100,7 @@ public async Task AtBuilderStage_MenuBarJson_SerializesCorrectly(CancellationTok // Act builder.Features.Menu.SetMenuBar(menuBar); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(initParameters.MenuBarJson).IsNotNull(); @@ -124,7 +125,7 @@ public async Task AtBuilderStage_SetMenuBar_EmptyItems_JsonIsNull(CancellationTo // Act builder.Features.Menu.SetMenuBar(new InfiniFrameMenuBar()); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Menu.MenuBar.Items.IsEmpty).IsTrue(); @@ -147,7 +148,7 @@ public async Task AtBuilderStage_SetMenuBar_ReplacesExisting(CancellationToken c new InfiniFrameMenuItem("new", "New") ] )); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Menu.MenuBar.Items.Length).IsEqualTo(1); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Menu/MenuItemEnableDisableTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Menu/MenuItemEnableDisableTests.cs similarity index 99% rename from tests/InfiniTests.InfiniFrame/Window/Features/Menu/MenuItemEnableDisableTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Menu/MenuItemEnableDisableTests.cs index 824663e90..f0956d3d1 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Menu/MenuItemEnableDisableTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Menu/MenuItemEnableDisableTests.cs @@ -2,6 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Menu; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Menu/MenuItemTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Menu/MenuItemTests.cs similarity index 100% rename from tests/InfiniTests.InfiniFrame/Window/Features/Menu/MenuItemTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Menu/MenuItemTests.cs diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Monitors/GetMainMonitorScreenDpiTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Monitors/GetMainMonitorScreenDpiTests.cs similarity index 97% rename from tests/InfiniTests.InfiniFrame/Window/Features/Monitors/GetMainMonitorScreenDpiTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Monitors/GetMainMonitorScreenDpiTests.cs index f78b85766..39659a808 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Monitors/GetMainMonitorScreenDpiTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Monitors/GetMainMonitorScreenDpiTests.cs @@ -2,6 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Monitors; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Monitors/GetMainMonitorTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Monitors/GetMainMonitorTests.cs similarity index 95% rename from tests/InfiniTests.InfiniFrame/Window/Features/Monitors/GetMainMonitorTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Monitors/GetMainMonitorTests.cs index 626226d8a..17b063426 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Monitors/GetMainMonitorTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Monitors/GetMainMonitorTests.cs @@ -2,6 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Monitors; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Monitors/GetMonitorsTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Monitors/GetMonitorsTests.cs similarity index 95% rename from tests/InfiniTests.InfiniFrame/Window/Features/Monitors/GetMonitorsTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Monitors/GetMonitorsTests.cs index 7a1930fef..27110ac4a 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Monitors/GetMonitorsTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Monitors/GetMonitorsTests.cs @@ -2,6 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Monitors; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Notifications/NotificationBuilderTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Notifications/NotificationBuilderTests.cs similarity index 87% rename from tests/InfiniTests.InfiniFrame/Window/Features/Notifications/NotificationBuilderTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Notifications/NotificationBuilderTests.cs index c58aded8c..7ebeea91e 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Notifications/NotificationBuilderTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Notifications/NotificationBuilderTests.cs @@ -2,7 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; namespace InfiniTests.InfiniFrame.Window.Features.Notifications; // --------------------------------------------------------------------------------------------------------------------- @@ -16,7 +17,7 @@ public async Task AtBuilderStage_DefaultNotificationIcon(CancellationToken ct) { // Act builder.Features.Notifications.SetDefaultNotificationIcon("/path/to/icon.png"); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Notifications.DefaultNotificationIcon).IsEqualTo("/path/to/icon.png"); @@ -31,7 +32,7 @@ public async Task AtBuilderStage_ClearDefaultNotificationIcon(CancellationToken // Act builder.Features.Notifications.SetDefaultNotificationIcon("/path/to/icon.png"); builder.Features.Notifications.SetDefaultNotificationIcon(null); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Notifications.DefaultNotificationIcon).IsNull(); @@ -45,7 +46,7 @@ public async Task AtBuilderStage_ExtensionAssignment_DefaultNotificationIcon(Can // Act IInfiniFrameWindowBuilder returnedBuilder = builder.SetDefaultNotificationIcon("/path/to/icon.png"); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Notifications.DefaultNotificationIcon).IsEqualTo("/path/to/icon.png"); @@ -63,7 +64,7 @@ public async Task AtBuilderStage_EnableNotifications_WithDefaultIcon(bool enable // Act builder.Features.Notifications.EnableNotifications(enable); builder.Features.Notifications.SetDefaultNotificationIcon("/path/to/icon.png"); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Notifications.IsNotificationsEnabled).IsEqualTo(enable); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Notifications/NotificationOperationTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Notifications/NotificationOperationTests.cs similarity index 97% rename from tests/InfiniTests.InfiniFrame/Window/Features/Notifications/NotificationOperationTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Notifications/NotificationOperationTests.cs index 9a6a1836e..b03282c89 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Notifications/NotificationOperationTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Notifications/NotificationOperationTests.cs @@ -2,6 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Notifications; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Notifications/NotificationsTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Notifications/NotificationsTests.cs similarity index 87% rename from tests/InfiniTests.InfiniFrame/Window/Features/Notifications/NotificationsTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Notifications/NotificationsTests.cs index 6aa3e032b..567f7072b 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Notifications/NotificationsTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Notifications/NotificationsTests.cs @@ -2,7 +2,10 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Notifications; // --------------------------------------------------------------------------------------------------------------------- @@ -18,7 +21,7 @@ public async Task AtBuilderStage_DirectAssignment(bool value, CancellationToken // Act builder.Features.Notifications.EnableNotifications(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Notifications.IsNotificationsEnabled).IsEqualTo(value); @@ -34,7 +37,7 @@ public async Task AtBuilderStage_ExtensionAssignment(bool value, CancellationTok // Act IInfiniFrameWindowBuilder returnedBuilder = builder.EnableNotifications(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Notifications.IsNotificationsEnabled).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Notifications/ShowMessageTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Notifications/ShowMessageTests.cs similarity index 98% rename from tests/InfiniTests.InfiniFrame/Window/Features/Notifications/ShowMessageTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Notifications/ShowMessageTests.cs index 3bc316cb6..c0d6d31a9 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Notifications/ShowMessageTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Notifications/ShowMessageTests.cs @@ -4,6 +4,8 @@ using System.Diagnostics.CodeAnalysis; using InfiniFrame; using InfiniFrame.NativeBridge.Dialogs; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Notifications; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Notifications/ShowNotificationOptionsTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Notifications/ShowNotificationOptionsTests.cs similarity index 100% rename from tests/InfiniTests.InfiniFrame/Window/Features/Notifications/ShowNotificationOptionsTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Notifications/ShowNotificationOptionsTests.cs diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Notifications/ShowNotificationTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Notifications/ShowNotificationTests.cs similarity index 96% rename from tests/InfiniTests.InfiniFrame/Window/Features/Notifications/ShowNotificationTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Notifications/ShowNotificationTests.cs index dec419b6f..6c09b6d56 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Notifications/ShowNotificationTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Notifications/ShowNotificationTests.cs @@ -2,6 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Notifications; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/PageNavigation/GetCurrentUrlTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/PageNavigation/GetCurrentUrlTests.cs similarity index 98% rename from tests/InfiniTests.InfiniFrame/Window/Features/PageNavigation/GetCurrentUrlTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/PageNavigation/GetCurrentUrlTests.cs index 30103296f..bd8dd9ae1 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/PageNavigation/GetCurrentUrlTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/PageNavigation/GetCurrentUrlTests.cs @@ -2,6 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.PageNavigation; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/PageNavigation/LoadAsyncTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/PageNavigation/LoadAsyncTests.cs similarity index 98% rename from tests/InfiniTests.InfiniFrame/Window/Features/PageNavigation/LoadAsyncTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/PageNavigation/LoadAsyncTests.cs index e34763c26..da1fd8686 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/PageNavigation/LoadAsyncTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/PageNavigation/LoadAsyncTests.cs @@ -2,6 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.PageNavigation; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/PageNavigation/LoadRawStringTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/PageNavigation/LoadRawStringTests.cs similarity index 97% rename from tests/InfiniTests.InfiniFrame/Window/Features/PageNavigation/LoadRawStringTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/PageNavigation/LoadRawStringTests.cs index 26cf8f577..6fc9a6053 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/PageNavigation/LoadRawStringTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/PageNavigation/LoadRawStringTests.cs @@ -2,6 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.PageNavigation; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/PageNavigation/LoadStringTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/PageNavigation/LoadStringTests.cs similarity index 97% rename from tests/InfiniTests.InfiniFrame/Window/Features/PageNavigation/LoadStringTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/PageNavigation/LoadStringTests.cs index 30ea9d398..3408b2a69 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/PageNavigation/LoadStringTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/PageNavigation/LoadStringTests.cs @@ -2,6 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.PageNavigation; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/PageNavigation/LoadUriTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/PageNavigation/LoadUriTests.cs similarity index 98% rename from tests/InfiniTests.InfiniFrame/Window/Features/PageNavigation/LoadUriTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/PageNavigation/LoadUriTests.cs index ee229a633..fa960c8a5 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/PageNavigation/LoadUriTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/PageNavigation/LoadUriTests.cs @@ -2,6 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.PageNavigation; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/PageNavigation/StartPageContentTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/PageNavigation/StartPageContentTests.cs similarity index 87% rename from tests/InfiniTests.InfiniFrame/Window/Features/PageNavigation/StartPageContentTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/PageNavigation/StartPageContentTests.cs index 1e01468bb..8e975db63 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/PageNavigation/StartPageContentTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/PageNavigation/StartPageContentTests.cs @@ -2,7 +2,10 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.PageNavigation; // --------------------------------------------------------------------------------------------------------------------- @@ -18,7 +21,7 @@ public async Task AtBuilderStage_DirectAssignment(string value, CancellationToke // Act builder.Features.PageNavigation.SetStartPageContent(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.PageNavigation.StartString).IsEqualTo(value); @@ -34,7 +37,7 @@ public async Task AtBuilderStage_ExtensionAssignment(string value, CancellationT // Act IInfiniFrameWindowBuilder returnedBuilder = builder.SetStartPageContent(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.PageNavigation.StartString).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/PageNavigation/StartPageUrlTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/PageNavigation/StartPageUrlTests.cs similarity index 86% rename from tests/InfiniTests.InfiniFrame/Window/Features/PageNavigation/StartPageUrlTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/PageNavigation/StartPageUrlTests.cs index d02e25591..1d88bc0ad 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/PageNavigation/StartPageUrlTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/PageNavigation/StartPageUrlTests.cs @@ -2,7 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; namespace InfiniTests.InfiniFrame.Window.Features.PageNavigation; // --------------------------------------------------------------------------------------------------------------------- @@ -18,7 +19,7 @@ public async Task AtBuilderStage_DirectAssignment(string value, CancellationToke // Act builder.Features.PageNavigation.SetStartPageUrl(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.PageNavigation.StartUrl).IsEqualTo(value); @@ -34,7 +35,7 @@ public async Task AtBuilderStage_ExtensionAssignment(string value, CancellationT // Act IInfiniFrameWindowBuilder returnedBuilder = builder.SetStartPageUrl(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.PageNavigation.StartUrl).IsEqualTo(value); @@ -52,7 +53,7 @@ public async Task AtBuilderStage_UriAssignment(string value, CancellationToken c // Act IInfiniFrameWindowBuilder returnedBuilder = builder.SetUrl(uri); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.PageNavigation.StartUrl).IsEqualTo(uri.ToString()); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/PageNavigation/TryLoadPathTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/PageNavigation/TryLoadPathTests.cs similarity index 96% rename from tests/InfiniTests.InfiniFrame/Window/Features/PageNavigation/TryLoadPathTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/PageNavigation/TryLoadPathTests.cs index 5e00fd99f..e68d2adfa 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/PageNavigation/TryLoadPathTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/PageNavigation/TryLoadPathTests.cs @@ -2,6 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.PageNavigation; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/PageNavigation/TryLoadUriTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/PageNavigation/TryLoadUriTests.cs similarity index 97% rename from tests/InfiniTests.InfiniFrame/Window/Features/PageNavigation/TryLoadUriTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/PageNavigation/TryLoadUriTests.cs index ecc3fff91..0e49dcd1c 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/PageNavigation/TryLoadUriTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/PageNavigation/TryLoadUriTests.cs @@ -2,6 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.PageNavigation; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Position/CenterOnCurrentMonitorTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Position/CenterOnCurrentMonitorTests.cs similarity index 95% rename from tests/InfiniTests.InfiniFrame/Window/Features/Position/CenterOnCurrentMonitorTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Position/CenterOnCurrentMonitorTests.cs index 563f5421b..7bd420d42 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Position/CenterOnCurrentMonitorTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Position/CenterOnCurrentMonitorTests.cs @@ -2,6 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; namespace InfiniTests.InfiniFrame.Window.Features.Position; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Position/CenterOnMonitorTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Position/CenterOnMonitorTests.cs similarity index 96% rename from tests/InfiniTests.InfiniFrame/Window/Features/Position/CenterOnMonitorTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Position/CenterOnMonitorTests.cs index 3b5d56613..6d95a568f 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Position/CenterOnMonitorTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Position/CenterOnMonitorTests.cs @@ -2,6 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; namespace InfiniTests.InfiniFrame.Window.Features.Position; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Position/CenterTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Position/CenterTests.cs similarity index 95% rename from tests/InfiniTests.InfiniFrame/Window/Features/Position/CenterTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Position/CenterTests.cs index 27f5aaf47..c55f1a40d 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Position/CenterTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Position/CenterTests.cs @@ -2,6 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; namespace InfiniTests.InfiniFrame.Window.Features.Position; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Position/CenteredOnMainMonitorTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Position/CenteredOnMainMonitorTests.cs similarity index 88% rename from tests/InfiniTests.InfiniFrame/Window/Features/Position/CenteredOnMainMonitorTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Position/CenteredOnMainMonitorTests.cs index a3cdbb8b1..13e902d29 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Position/CenteredOnMainMonitorTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Position/CenteredOnMainMonitorTests.cs @@ -2,7 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; namespace InfiniTests.InfiniFrame.Window.Features.Position; // --------------------------------------------------------------------------------------------------------------------- @@ -18,7 +19,7 @@ public async Task AtBuilderStage_DirectAssignment(bool value, CancellationToken // Act builder.Features.Position.CenteredOnMainMonitor(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Position.StartCentered).IsEqualTo(value); @@ -36,7 +37,7 @@ public async Task AtBuilderStage_ExtensionAssignment(bool value, CancellationTok // Act IInfiniFrameWindowBuilder returnedBuilder = builder.CenteredOnMainMonitor(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Position.StartCentered).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Position/MoveWithinCurrentMonitorAreaTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Position/MoveWithinCurrentMonitorAreaTests.cs similarity index 96% rename from tests/InfiniTests.InfiniFrame/Window/Features/Position/MoveWithinCurrentMonitorAreaTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Position/MoveWithinCurrentMonitorAreaTests.cs index d7a02b1ed..b097d3b2c 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Position/MoveWithinCurrentMonitorAreaTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Position/MoveWithinCurrentMonitorAreaTests.cs @@ -3,6 +3,9 @@ // --------------------------------------------------------------------------------------------------------------------- using System.Drawing; using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; namespace InfiniTests.InfiniFrame.Window.Features.Position; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Position/OffsetTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Position/OffsetTests.cs similarity index 97% rename from tests/InfiniTests.InfiniFrame/Window/Features/Position/OffsetTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Position/OffsetTests.cs index 87ab3375b..0808dd3ac 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Position/OffsetTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Position/OffsetTests.cs @@ -3,6 +3,9 @@ // --------------------------------------------------------------------------------------------------------------------- using System.Drawing; using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; namespace InfiniTests.InfiniFrame.Window.Features.Position; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Position/SetLeftTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Position/SetLeftTests.cs similarity index 90% rename from tests/InfiniTests.InfiniFrame/Window/Features/Position/SetLeftTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Position/SetLeftTests.cs index e30365191..9e5c3f858 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Position/SetLeftTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Position/SetLeftTests.cs @@ -2,7 +2,11 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; namespace InfiniTests.InfiniFrame.Window.Features.Position; // --------------------------------------------------------------------------------------------------------------------- @@ -18,7 +22,7 @@ public async Task AtBuilderStage_DirectAssignment(int value, CancellationToken c // Act builder.Features.Position.SetLeft(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Position.Left).IsEqualTo(value); @@ -36,7 +40,7 @@ public async Task AtBuilderStage_ExtensionAssignment(int value, CancellationToke // Act IInfiniFrameWindowBuilder returnedBuilder = builder.SetLeft(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Position.Left).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Position/SetLocationTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Position/SetLocationTests.cs similarity index 92% rename from tests/InfiniTests.InfiniFrame/Window/Features/Position/SetLocationTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Position/SetLocationTests.cs index f48161c7f..dc292044a 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Position/SetLocationTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Position/SetLocationTests.cs @@ -3,7 +3,11 @@ // --------------------------------------------------------------------------------------------------------------------- using System.Drawing; using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; namespace InfiniTests.InfiniFrame.Window.Features.Position; // --------------------------------------------------------------------------------------------------------------------- @@ -19,7 +23,7 @@ public async Task AtBuilderStage_DirectAssignment(int left, int top, Cancellatio // Act builder.Features.Position.SetLocation(left, top); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Position.Left).IsEqualTo(left); @@ -40,7 +44,7 @@ public async Task AtBuilderStage_ExtensionAssignment(int left, int top, Cancella // Act IInfiniFrameWindowBuilder returnedBuilder = builder.SetLocation(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Position.Left).IsEqualTo(left); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Position/SetTopTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Position/SetTopTests.cs similarity index 89% rename from tests/InfiniTests.InfiniFrame/Window/Features/Position/SetTopTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Position/SetTopTests.cs index 19921981b..298886bb5 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Position/SetTopTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Position/SetTopTests.cs @@ -2,7 +2,10 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Position; // --------------------------------------------------------------------------------------------------------------------- @@ -18,7 +21,7 @@ public async Task AtBuilderStage_DirectAssignment(int value, CancellationToken c // Act builder.Features.Position.SetTop(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Position.Top).IsEqualTo(value); @@ -36,7 +39,7 @@ public async Task AtBuilderStage_ExtensionAssignment(int value, CancellationToke // Act IInfiniFrameWindowBuilder returnedBuilder = builder.SetTop(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Position.Top).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Position/UseOsDefaultLocationTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Position/UseOsDefaultLocationTests.cs similarity index 86% rename from tests/InfiniTests.InfiniFrame/Window/Features/Position/UseOsDefaultLocationTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Position/UseOsDefaultLocationTests.cs index 1ffc65c86..613d3f735 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Position/UseOsDefaultLocationTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Position/UseOsDefaultLocationTests.cs @@ -2,7 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; namespace InfiniTests.InfiniFrame.Window.Features.Position; // --------------------------------------------------------------------------------------------------------------------- @@ -18,7 +19,7 @@ public async Task AtBuilderStage_DirectAssignment(bool value, CancellationToken // Act builder.Features.Position.UseOsDefaultLocation(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Position.StartAtOsDefaultLocation).IsEqualTo(value); @@ -34,7 +35,7 @@ public async Task AtBuilderStage_ExtensionAssignment(bool value, CancellationTok // Act IInfiniFrameWindowBuilder returnedBuilder = builder.UseOsDefaultLocation(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Position.StartAtOsDefaultLocation).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Size/ResizeTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/ResizeTests.cs similarity index 96% rename from tests/InfiniTests.InfiniFrame/Window/Features/Size/ResizeTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/ResizeTests.cs index e05ab7c87..8806281ca 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Size/ResizeTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/ResizeTests.cs @@ -2,6 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; namespace InfiniTests.InfiniFrame.Window.Features.Size; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Size/ResizeViewportTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/ResizeViewportTests.cs similarity index 98% rename from tests/InfiniTests.InfiniFrame/Window/Features/Size/ResizeViewportTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/ResizeViewportTests.cs index bd3f0670b..2ac73d399 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Size/ResizeViewportTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/ResizeViewportTests.cs @@ -3,6 +3,9 @@ // --------------------------------------------------------------------------------------------------------------------- using System.Text.RegularExpressions; using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; namespace InfiniTests.InfiniFrame.Window.Features.Size; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Size/SetHeightTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/SetHeightTests.cs similarity index 90% rename from tests/InfiniTests.InfiniFrame/Window/Features/Size/SetHeightTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/SetHeightTests.cs index db3a5dbef..d9f21a9bc 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Size/SetHeightTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/SetHeightTests.cs @@ -2,7 +2,11 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; namespace InfiniTests.InfiniFrame.Window.Features.Size; // --------------------------------------------------------------------------------------------------------------------- @@ -18,7 +22,7 @@ public async Task AtBuilderStage_DirectAssignment(int value, CancellationToken c // Act builder.Features.Size.SetHeight(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Size.Height).IsEqualTo(value); @@ -36,7 +40,7 @@ public async Task AtBuilderStage_ExtensionAssignment(int value, CancellationToke // Act IInfiniFrameWindowBuilder returnedBuilder = builder.SetHeight(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Size.Height).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Size/SetMaxHeightTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/SetMaxHeightTests.cs similarity index 89% rename from tests/InfiniTests.InfiniFrame/Window/Features/Size/SetMaxHeightTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/SetMaxHeightTests.cs index 7e7a4da45..df7ff4b56 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Size/SetMaxHeightTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/SetMaxHeightTests.cs @@ -2,7 +2,11 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; namespace InfiniTests.InfiniFrame.Window.Features.Size; // --------------------------------------------------------------------------------------------------------------------- @@ -18,7 +22,7 @@ public async Task AtBuilderStage_DirectAssignment(int value, CancellationToken c // Act builder.Features.Size.SetMaxHeight(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Size.MaxHeight).IsEqualTo(value); @@ -34,7 +38,7 @@ public async Task AtBuilderStage_ExtensionAssignment(int value, CancellationToke // Act IInfiniFrameWindowBuilder returnedBuilder = builder.SetMaxHeight(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Size.MaxHeight).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Size/SetMaxSizeTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/SetMaxSizeTests.cs similarity index 89% rename from tests/InfiniTests.InfiniFrame/Window/Features/Size/SetMaxSizeTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/SetMaxSizeTests.cs index 69595dba0..15d321cfa 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Size/SetMaxSizeTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/SetMaxSizeTests.cs @@ -2,7 +2,11 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; namespace InfiniTests.InfiniFrame.Window.Features.Size; // --------------------------------------------------------------------------------------------------------------------- @@ -18,7 +22,7 @@ public async Task AtBuilderStage_DirectAssignment(int width, int height, Cancell // Act builder.Features.Size.SetMaxSize(width, height); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Size.MaxWidth).IsEqualTo(width); @@ -36,7 +40,7 @@ public async Task AtBuilderStage_ExtensionAssignment(int width, int height, Canc // Act IInfiniFrameWindowBuilder returnedBuilder = builder.SetMaxSize(width, height); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Size.MaxWidth).IsEqualTo(width); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Size/SetMaxWidthTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/SetMaxWidthTests.cs similarity index 89% rename from tests/InfiniTests.InfiniFrame/Window/Features/Size/SetMaxWidthTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/SetMaxWidthTests.cs index 3358b2500..7442077bf 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Size/SetMaxWidthTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/SetMaxWidthTests.cs @@ -2,7 +2,11 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; namespace InfiniTests.InfiniFrame.Window.Features.Size; // --------------------------------------------------------------------------------------------------------------------- @@ -18,7 +22,7 @@ public async Task AtBuilderStage_DirectAssignment(int value, CancellationToken c // Act builder.Features.Size.SetMaxWidth(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Size.MaxWidth).IsEqualTo(value); @@ -34,7 +38,7 @@ public async Task AtBuilderStage_ExtensionAssignment(int value, CancellationToke // Act IInfiniFrameWindowBuilder returnedBuilder = builder.SetMaxWidth(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Size.MaxWidth).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Size/SetMinHeightTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/SetMinHeightTests.cs similarity index 89% rename from tests/InfiniTests.InfiniFrame/Window/Features/Size/SetMinHeightTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/SetMinHeightTests.cs index a0557ff1f..f5a954c1b 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Size/SetMinHeightTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/SetMinHeightTests.cs @@ -2,7 +2,11 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; namespace InfiniTests.InfiniFrame.Window.Features.Size; // --------------------------------------------------------------------------------------------------------------------- @@ -18,7 +22,7 @@ public async Task AtBuilderStage_DirectAssignment(int value, CancellationToken c // Act builder.Features.Size.SetMinHeight(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Size.MinHeight).IsEqualTo(value); @@ -34,7 +38,7 @@ public async Task AtBuilderStage_ExtensionAssignment(int value, CancellationToke // Act IInfiniFrameWindowBuilder returnedBuilder = builder.SetMinHeight(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Size.MinHeight).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Size/SetMinSizeTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/SetMinSizeTests.cs similarity index 89% rename from tests/InfiniTests.InfiniFrame/Window/Features/Size/SetMinSizeTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/SetMinSizeTests.cs index fb14b38c4..9827504cc 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Size/SetMinSizeTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/SetMinSizeTests.cs @@ -2,7 +2,11 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; namespace InfiniTests.InfiniFrame.Window.Features.Size; // --------------------------------------------------------------------------------------------------------------------- @@ -18,7 +22,7 @@ public async Task AtBuilderStage_DirectAssignment(int width, int height, Cancell // Act builder.Features.Size.SetMinSize(width, height); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Size.MinWidth).IsEqualTo(width); @@ -36,7 +40,7 @@ public async Task AtBuilderStage_ExtensionAssignment(int width, int height, Canc // Act IInfiniFrameWindowBuilder returnedBuilder = builder.SetMinSize(width, height); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Size.MinWidth).IsEqualTo(width); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Size/SetMinWidthTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/SetMinWidthTests.cs similarity index 89% rename from tests/InfiniTests.InfiniFrame/Window/Features/Size/SetMinWidthTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/SetMinWidthTests.cs index 798fa66bc..3e1eeb8cc 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Size/SetMinWidthTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/SetMinWidthTests.cs @@ -2,7 +2,11 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; namespace InfiniTests.InfiniFrame.Window.Features.Size; // --------------------------------------------------------------------------------------------------------------------- @@ -18,7 +22,7 @@ public async Task AtBuilderStage_DirectAssignment(int value, CancellationToken c // Act builder.Features.Size.SetMinWidth(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Size.MinWidth).IsEqualTo(value); @@ -34,7 +38,7 @@ public async Task AtBuilderStage_ExtensionAssignment(int value, CancellationToke // Act IInfiniFrameWindowBuilder returnedBuilder = builder.SetMinWidth(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Size.MinWidth).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Size/SetResizableTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/SetResizableTests.cs similarity index 90% rename from tests/InfiniTests.InfiniFrame/Window/Features/Size/SetResizableTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/SetResizableTests.cs index 5a59dc609..3c7a31468 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Size/SetResizableTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/SetResizableTests.cs @@ -2,7 +2,11 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; namespace InfiniTests.InfiniFrame.Window.Features.Size; // --------------------------------------------------------------------------------------------------------------------- @@ -18,7 +22,7 @@ public async Task AtBuilderStage_DirectAssignment(bool value, CancellationToken // Act builder.Features.Size.SetResizable(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Size.IsResizable).IsEqualTo(value); @@ -34,7 +38,7 @@ public async Task AtBuilderStage_ExtensionAssignment(bool value, CancellationTok // Act IInfiniFrameWindowBuilder returnedBuilder = builder.SetResizable(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Size.IsResizable).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Size/SetSizeTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/SetSizeTests.cs similarity index 93% rename from tests/InfiniTests.InfiniFrame/Window/Features/Size/SetSizeTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/SetSizeTests.cs index 1897aabf9..1d3746ee6 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Size/SetSizeTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/SetSizeTests.cs @@ -2,7 +2,11 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; namespace InfiniTests.InfiniFrame.Window.Features.Size; // --------------------------------------------------------------------------------------------------------------------- @@ -18,7 +22,7 @@ public async Task AtBuilderStage_DirectAssignment(int width, int height, Cancell // Act builder.Features.Size.SetSize(width, height); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Size.Width).IsEqualTo(width); @@ -39,7 +43,7 @@ public async Task AtBuilderStage_ExtensionAssignment(int width, int height, Canc // Act IInfiniFrameWindowBuilder returnedBuilder = builder.SetSize(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Size.Width).IsEqualTo(width); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Size/SetWidthTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/SetWidthTests.cs similarity index 89% rename from tests/InfiniTests.InfiniFrame/Window/Features/Size/SetWidthTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/SetWidthTests.cs index 96f65dcb7..8803e8a28 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Size/SetWidthTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/SetWidthTests.cs @@ -2,7 +2,11 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; namespace InfiniTests.InfiniFrame.Window.Features.Size; // --------------------------------------------------------------------------------------------------------------------- @@ -18,7 +22,7 @@ public async Task AtBuilderStage_DirectAssignment(int value, CancellationToken c // Act builder.Features.Size.SetWidth(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Size.Width).IsEqualTo(value); @@ -36,7 +40,7 @@ public async Task AtBuilderStage_ExtensionAssignment(int value, CancellationToke // Act IInfiniFrameWindowBuilder returnedBuilder = builder.SetWidth(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Size.Width).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Size/UseOsDefaultSizeTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/UseOsDefaultSizeTests.cs similarity index 86% rename from tests/InfiniTests.InfiniFrame/Window/Features/Size/UseOsDefaultSizeTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/UseOsDefaultSizeTests.cs index 3195e2bc3..24067ba42 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Size/UseOsDefaultSizeTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Size/UseOsDefaultSizeTests.cs @@ -2,7 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; namespace InfiniTests.InfiniFrame.Window.Features.Size; // --------------------------------------------------------------------------------------------------------------------- @@ -18,7 +19,7 @@ public async Task AtBuilderStage_DirectAssignment(bool value, CancellationToken // Act builder.Features.Size.UseOsDefaultSize(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Size.StartWithOsDefaultSize).IsEqualTo(value); @@ -34,7 +35,7 @@ public async Task AtBuilderStage_ExtensionAssignment(bool value, CancellationTok // Act IInfiniFrameWindowBuilder returnedBuilder = builder.UseOsDefaultSize(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.Size.StartWithOsDefaultSize).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/State/FullScreenTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/State/FullScreenTests.cs similarity index 91% rename from tests/InfiniTests.InfiniFrame/Window/Features/State/FullScreenTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/State/FullScreenTests.cs index b10aadc01..f62259aa0 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/State/FullScreenTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/State/FullScreenTests.cs @@ -2,7 +2,10 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.State; // --------------------------------------------------------------------------------------------------------------------- @@ -19,7 +22,7 @@ public async Task AtBuilderStage_DirectAssignment(bool value, CancellationToken // Act builder.Features.State.SetFullScreen(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.State.StartFullScreen).IsEqualTo(value); @@ -35,7 +38,7 @@ public async Task AtBuilderStage_ExtensionAssignment(bool value, CancellationTok // Act IInfiniFrameWindowBuilder returnedBuilder = builder.SetFullScreen(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.State.StartFullScreen).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/State/MaximizedTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/State/MaximizedTests.cs similarity index 91% rename from tests/InfiniTests.InfiniFrame/Window/Features/State/MaximizedTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/State/MaximizedTests.cs index 08d94ca39..65a9014db 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/State/MaximizedTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/State/MaximizedTests.cs @@ -2,7 +2,10 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.State; // --------------------------------------------------------------------------------------------------------------------- @@ -19,7 +22,7 @@ public async Task AtBuilderStage_DirectAssignment(bool value, CancellationToken // Act builder.Features.State.SetMaximized(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.State.StartMaximized).IsEqualTo(value); @@ -35,7 +38,7 @@ public async Task AtBuilderStage_ExtensionAssignment(bool value, CancellationTok // Act IInfiniFrameWindowBuilder returnedBuilder = builder.SetMaximized(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.State.StartMaximized).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/State/MinimizedTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/State/MinimizedTests.cs similarity index 92% rename from tests/InfiniTests.InfiniFrame/Window/Features/State/MinimizedTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/State/MinimizedTests.cs index b051f40e4..d3b09e6c8 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/State/MinimizedTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/State/MinimizedTests.cs @@ -2,7 +2,10 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.State; // --------------------------------------------------------------------------------------------------------------------- @@ -19,7 +22,7 @@ public async Task AtBuilderStage_DirectAssignment(bool value, CancellationToken // Act builder.Features.State.SetMinimized(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.State.StartMinimized).IsEqualTo(value); @@ -35,7 +38,7 @@ public async Task AtBuilderStage_ExtensionAssignment(bool value, CancellationTok // Act IInfiniFrameWindowBuilder returnedBuilder = builder.SetMinimized(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.State.StartMinimized).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/State/SetFocusedTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/State/SetFocusedTests.cs similarity index 95% rename from tests/InfiniTests.InfiniFrame/Window/Features/State/SetFocusedTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/State/SetFocusedTests.cs index af3c7b826..8cd3065c4 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/State/SetFocusedTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/State/SetFocusedTests.cs @@ -2,6 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.State; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/State/ToggleMaximizedTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/State/ToggleMaximizedTests.cs similarity index 96% rename from tests/InfiniTests.InfiniFrame/Window/Features/State/ToggleMaximizedTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/State/ToggleMaximizedTests.cs index 97b860004..4b66d0092 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/State/ToggleMaximizedTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/State/ToggleMaximizedTests.cs @@ -2,6 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.State; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/State/TopMostTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/State/TopMostTests.cs similarity index 90% rename from tests/InfiniTests.InfiniFrame/Window/Features/State/TopMostTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/State/TopMostTests.cs index 730078319..03e5ef179 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/State/TopMostTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/State/TopMostTests.cs @@ -2,7 +2,10 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.State; // --------------------------------------------------------------------------------------------------------------------- @@ -19,7 +22,7 @@ public async Task AtBuilderStage_DirectAssignment(bool value, CancellationToken // Act builder.Features.State.SetTopMost(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.State.StartTopMost).IsEqualTo(value); @@ -35,7 +38,7 @@ public async Task AtBuilderStage_ExtensionAssignment(bool value, CancellationTok // Act IInfiniFrameWindowBuilder returnedBuilder = builder.SetTopMost(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.State.StartTopMost).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/State/ZoomDpiParityTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/State/ZoomDpiParityTests.cs similarity index 98% rename from tests/InfiniTests.InfiniFrame/Window/Features/State/ZoomDpiParityTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/State/ZoomDpiParityTests.cs index 16c948b15..cc6735ac8 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/State/ZoomDpiParityTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/State/ZoomDpiParityTests.cs @@ -2,6 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.State; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/State/ZoomFactorBoundaryTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/State/ZoomFactorBoundaryTests.cs similarity index 88% rename from tests/InfiniTests.InfiniFrame/Window/Features/State/ZoomFactorBoundaryTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/State/ZoomFactorBoundaryTests.cs index 8fb030bc1..01ab220e4 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/State/ZoomFactorBoundaryTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/State/ZoomFactorBoundaryTests.cs @@ -2,7 +2,10 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.State; // --------------------------------------------------------------------------------------------------------------------- @@ -19,7 +22,7 @@ public async Task Builder_StoresValidZoomRange(int value, CancellationToken ct) // Act builder.Features.State.SetZoomFactor(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.State.ZoomFactor).IsEqualTo(value); @@ -35,7 +38,7 @@ public async Task Builder_StoresOutOfRangeZoom(int value, CancellationToken ct) // Act builder.Features.State.SetZoomFactor(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.State.ZoomFactor).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/State/ZoomFactorTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/State/ZoomFactorTests.cs similarity index 91% rename from tests/InfiniTests.InfiniFrame/Window/Features/State/ZoomFactorTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/State/ZoomFactorTests.cs index be1e50b8b..9e00d7edc 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/State/ZoomFactorTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/State/ZoomFactorTests.cs @@ -2,7 +2,10 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.State; // --------------------------------------------------------------------------------------------------------------------- @@ -21,7 +24,7 @@ public async Task AtBuilderStage_DirectAssignment(int value, CancellationToken c // Act builder.Features.State.SetZoomFactor(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.State.ZoomFactor).IsEqualTo(value); @@ -39,7 +42,7 @@ public async Task AtBuilderStage_ExtensionAssignment(int value, CancellationToke // Act IInfiniFrameWindowBuilder returnedBuilder = builder.SetZoomFactor(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.State.ZoomFactor).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/State/ZoomTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/State/ZoomTests.cs similarity index 90% rename from tests/InfiniTests.InfiniFrame/Window/Features/State/ZoomTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/State/ZoomTests.cs index 69bfe062b..50e8be6e1 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/State/ZoomTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/State/ZoomTests.cs @@ -2,7 +2,10 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.State; // --------------------------------------------------------------------------------------------------------------------- @@ -19,7 +22,7 @@ public async Task AtBuilderStage_DirectAssignment(bool value, CancellationToken // Act builder.Features.State.EnableZoom(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.State.IsZoomEnabled).IsEqualTo(value); @@ -35,7 +38,7 @@ public async Task AtBuilderStage_ExtensionAssignment(bool value, CancellationTok // Act IInfiniFrameWindowBuilder returnedBuilder = builder.EnableZoom(value); - InfiniFrameNativeParameters initParameters = builder.CollectNativeParameters(); + InfiniFrameNativeWindowParameters initParameters = builder.CollectNativeParameters(); // Assert await Assert.That(builder.Features.State.IsZoomEnabled).IsEqualTo(value); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Taskbar/TaskbarCapabilityTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Taskbar/TaskbarCapabilityTests.cs similarity index 98% rename from tests/InfiniTests.InfiniFrame/Window/Features/Taskbar/TaskbarCapabilityTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Taskbar/TaskbarCapabilityTests.cs index e9f5d955e..18c240e0e 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Taskbar/TaskbarCapabilityTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Taskbar/TaskbarCapabilityTests.cs @@ -2,6 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Taskbar; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Taskbar/TaskbarFlashTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Taskbar/TaskbarFlashTests.cs similarity index 97% rename from tests/InfiniTests.InfiniFrame/Window/Features/Taskbar/TaskbarFlashTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Taskbar/TaskbarFlashTests.cs index 713957153..a65ec4617 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Taskbar/TaskbarFlashTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Taskbar/TaskbarFlashTests.cs @@ -2,6 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Taskbar; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Taskbar/TaskbarPlatformBehaviorTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Taskbar/TaskbarPlatformBehaviorTests.cs similarity index 98% rename from tests/InfiniTests.InfiniFrame/Window/Features/Taskbar/TaskbarPlatformBehaviorTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Taskbar/TaskbarPlatformBehaviorTests.cs index 257ea5360..ef5806527 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Taskbar/TaskbarPlatformBehaviorTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Taskbar/TaskbarPlatformBehaviorTests.cs @@ -2,6 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Taskbar; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Taskbar/TaskbarProgressTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Taskbar/TaskbarProgressTests.cs similarity index 98% rename from tests/InfiniTests.InfiniFrame/Window/Features/Taskbar/TaskbarProgressTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/Taskbar/TaskbarProgressTests.cs index 34e766702..4c071aeeb 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Taskbar/TaskbarProgressTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/Taskbar/TaskbarProgressTests.cs @@ -2,6 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window.Features.Taskbar; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/CamelCaseEnumWebMessageJsonConverterTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/CamelCaseEnumWebMessageJsonConverterTests.cs similarity index 98% rename from tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/CamelCaseEnumWebMessageJsonConverterTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/CamelCaseEnumWebMessageJsonConverterTests.cs index 8a717e347..1029a3c67 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/CamelCaseEnumWebMessageJsonConverterTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/CamelCaseEnumWebMessageJsonConverterTests.cs @@ -4,6 +4,7 @@ using System.Text.Json; using InfiniFrame; using InfiniFrame.Debugging; +using WindowFeatureWebMessageJsonContext = InfiniFrame.Window.Features.WebMessaging.WindowFeatureWebMessageJsonContext; namespace InfiniTests.InfiniFrame.Window.Features.WebMessaging; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/FeatureMembers.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/FeatureMembers.cs similarity index 100% rename from tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/FeatureMembers.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/FeatureMembers.cs diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/Handlers/GetMessageWebMessageHandlerTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/Handlers/GetMessageWebMessageHandlerTests.cs similarity index 95% rename from tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/Handlers/GetMessageWebMessageHandlerTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/Handlers/GetMessageWebMessageHandlerTests.cs index d95deec45..1a310099d 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/Handlers/GetMessageWebMessageHandlerTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/Handlers/GetMessageWebMessageHandlerTests.cs @@ -3,9 +3,12 @@ // --------------------------------------------------------------------------------------------------------------------- using System.Text.Json; using InfiniFrame; -using InfiniFrame.Interop; -using InfiniFrame.NativeBridge.Parameters; -using InfiniTests.Substitutes; +using InfiniFrame.Js.Interop; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniFrame.Window.Events; +using InfiniFrame.Window.Features.WebMessaging.Handlers; +using InfiniTests.TestSupport.Substitutes; using Microsoft.Extensions.Logging.Abstractions; namespace InfiniTests.InfiniFrame.Window.Features.WebMessaging.Handlers; @@ -157,7 +160,7 @@ private static (InfiniFrameWindowBuilder Builder, InfiniFrameEvents Events, Reco .BindToBuilder(builder); var events = new InfiniFrameEvents(eventsStore, NullLogger.Instance); - var nativeParameters = default(InfiniFrameNativeParameters); + var nativeParameters = default(InfiniFrameNativeWindowParameters); events.AssignToNativeParameters(ref nativeParameters); events.AssignToWindow(window.Window); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/Handlers/MessageHandlersTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/Handlers/MessageHandlersTests.cs similarity index 93% rename from tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/Handlers/MessageHandlersTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/Handlers/MessageHandlersTests.cs index cf65d4813..7334fc8f5 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/Handlers/MessageHandlersTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/Handlers/MessageHandlersTests.cs @@ -1,10 +1,12 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame; -using InfiniFrame.Interop; -using InfiniFrame.NativeBridge.Parameters; -using InfiniTests.Substitutes; +using InfiniFrame.Js.Interop; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniFrame.Window.Events; +using InfiniFrame.Window.Features.WebMessaging.Handlers; +using InfiniTests.TestSupport.Substitutes; using Microsoft.Extensions.Logging.Abstractions; namespace InfiniTests.InfiniFrame.Window.Features.WebMessaging.Handlers; @@ -88,7 +90,7 @@ private static (InfiniFrameWindowBuilder Builder, InfiniFrameEvents Events, Reco .BindToBuilder(builder); var events = new InfiniFrameEvents(eventsStore, NullLogger.Instance); - var nativeParameters = default(InfiniFrameNativeParameters); + var nativeParameters = default(InfiniFrameNativeWindowParameters); events.AssignToNativeParameters(ref nativeParameters); events.AssignToWindow(window.Window); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/Handlers/OpenExternalTargetWebMessageHandlerTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/Handlers/OpenExternalTargetWebMessageHandlerTests.cs similarity index 97% rename from tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/Handlers/OpenExternalTargetWebMessageHandlerTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/Handlers/OpenExternalTargetWebMessageHandlerTests.cs index 4df082940..f89d7da29 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/Handlers/OpenExternalTargetWebMessageHandlerTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/Handlers/OpenExternalTargetWebMessageHandlerTests.cs @@ -3,10 +3,14 @@ // --------------------------------------------------------------------------------------------------------------------- using System.Diagnostics; using InfiniFrame; -using InfiniFrame.Interop; +using InfiniFrame.Js.Interop; using InfiniFrame.NativeBridge.Handles; -using InfiniFrame.NativeBridge.Parameters; -using InfiniTests.Substitutes; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniFrame.Window.Events; +using InfiniFrame.Window.Features.WebMessaging.Handlers; +using InfiniFrame.Window.Interop; +using InfiniTests.TestSupport.Substitutes; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging.Abstractions; @@ -132,7 +136,7 @@ public async Task HandleWebMessage_PublicUrl_Http_OpensBrowser(CancellationToken builder.RegisterOpenExternalTargetWebMessageHandler(); var eventsStore = (InfiniFrameEventsStore)builder.EventsStore; var events = new InfiniFrameEvents(eventsStore, NullLogger.Instance); - var nativeParameters = default(InfiniFrameNativeParameters); + var nativeParameters = default(InfiniFrameNativeWindowParameters); events.AssignToNativeParameters(ref nativeParameters); var windowStub = new WindowWithServiceProviderStub(serviceProvider); @@ -298,7 +302,7 @@ private static (InfiniFrameWindowBuilder Builder, InfiniFrameEvents Events, Reco .BindToBuilder(builder); var events = new InfiniFrameEvents(eventsStore, NullLogger.Instance); - var nativeParameters = default(InfiniFrameNativeParameters); + var nativeParameters = default(InfiniFrameNativeWindowParameters); events.AssignToNativeParameters(ref nativeParameters); events.AssignToWindow(window.Window); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/Handlers/TitleChangedWebMessageHandlerTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/Handlers/TitleChangedWebMessageHandlerTests.cs similarity index 92% rename from tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/Handlers/TitleChangedWebMessageHandlerTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/Handlers/TitleChangedWebMessageHandlerTests.cs index 2082a994f..ba11f9588 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/Handlers/TitleChangedWebMessageHandlerTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/Handlers/TitleChangedWebMessageHandlerTests.cs @@ -1,10 +1,12 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame; -using InfiniFrame.Interop; -using InfiniFrame.NativeBridge.Parameters; -using InfiniTests.Substitutes; +using InfiniFrame.Js.Interop; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniFrame.Window.Events; +using InfiniFrame.Window.Features.WebMessaging.Handlers; +using InfiniTests.TestSupport.Substitutes; using Microsoft.Extensions.Logging.Abstractions; namespace InfiniTests.InfiniFrame.Window.Features.WebMessaging.Handlers; @@ -75,7 +77,7 @@ private static (InfiniFrameWindowBuilder Builder, InfiniFrameEvents Events, Reco .BindToBuilder(builder); var events = new InfiniFrameEvents(eventsStore, NullLogger.Instance); - var nativeParameters = default(InfiniFrameNativeParameters); + var nativeParameters = default(InfiniFrameNativeWindowParameters); events.AssignToNativeParameters(ref nativeParameters); events.AssignToWindow(window.Window); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/Handlers/WebMessageDispatcherTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/Handlers/WebMessageDispatcherTests.cs similarity index 99% rename from tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/Handlers/WebMessageDispatcherTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/Handlers/WebMessageDispatcherTests.cs index adb0ef5ec..de81c861c 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/Handlers/WebMessageDispatcherTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/Handlers/WebMessageDispatcherTests.cs @@ -3,6 +3,8 @@ // --------------------------------------------------------------------------------------------------------------------- using System.Text.Json; using InfiniFrame; +using InfiniFrame.Window.Features.WebMessaging.Handlers; +using InfiniTests.TestSupport; namespace InfiniTests.InfiniFrame.Window.Features.WebMessaging.Handlers; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/Handlers/WindowFeatureDispatcherCommandTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/Handlers/WindowFeatureDispatcherCommandTests.cs similarity index 99% rename from tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/Handlers/WindowFeatureDispatcherCommandTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/Handlers/WindowFeatureDispatcherCommandTests.cs index 0b72e1189..6158d1254 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/Handlers/WindowFeatureDispatcherCommandTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/Handlers/WindowFeatureDispatcherCommandTests.cs @@ -3,6 +3,8 @@ // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; using System.Text.Json; +using InfiniFrame.Window.Features.WebMessaging.Handlers; +using InfiniTests.TestSupport; namespace InfiniTests.InfiniFrame.Window.Features.WebMessaging.Handlers; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/Handlers/WindowFeatureWebMessageDispatcherBaseTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/Handlers/WindowFeatureWebMessageDispatcherBaseTests.cs similarity index 99% rename from tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/Handlers/WindowFeatureWebMessageDispatcherBaseTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/Handlers/WindowFeatureWebMessageDispatcherBaseTests.cs index 2e86db575..39bc3efdc 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/Handlers/WindowFeatureWebMessageDispatcherBaseTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/Handlers/WindowFeatureWebMessageDispatcherBaseTests.cs @@ -3,6 +3,7 @@ // --------------------------------------------------------------------------------------------------------------------- using System.Text.Json; using InfiniFrame; +using InfiniFrame.Window.Features.WebMessaging.Handlers; namespace InfiniTests.InfiniFrame.Window.Features.WebMessaging.Handlers; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/Handlers/WindowFeatureWebMessageHandlerTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/Handlers/WindowFeatureWebMessageHandlerTests.cs similarity index 97% rename from tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/Handlers/WindowFeatureWebMessageHandlerTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/Handlers/WindowFeatureWebMessageHandlerTests.cs index a45dfdeab..2f9ef7305 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/Handlers/WindowFeatureWebMessageHandlerTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/Handlers/WindowFeatureWebMessageHandlerTests.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame; +using InfiniFrame.Window.Features.WebMessaging.Handlers; namespace InfiniTests.InfiniFrame.Window.Features.WebMessaging.Handlers; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/Handlers/WindowFeatureWebMessageRouterTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/Handlers/WindowFeatureWebMessageRouterTests.cs similarity index 99% rename from tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/Handlers/WindowFeatureWebMessageRouterTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/Handlers/WindowFeatureWebMessageRouterTests.cs index d6c614405..24138ef40 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/Handlers/WindowFeatureWebMessageRouterTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/Handlers/WindowFeatureWebMessageRouterTests.cs @@ -6,6 +6,8 @@ using InfiniFrame.Debugging; using System.Drawing; using System.Text.Json; +using InfiniFrame.Window.Features.WebMessaging.Handlers; +using InfiniTests.TestSupport; namespace InfiniTests.InfiniFrame.Window.Features.WebMessaging.Handlers; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/Handlers/WindowRegistrationStateMachineTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/Handlers/WindowRegistrationStateMachineTests.cs similarity index 98% rename from tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/Handlers/WindowRegistrationStateMachineTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/Handlers/WindowRegistrationStateMachineTests.cs index bd52dba0e..62833b246 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/Handlers/WindowRegistrationStateMachineTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/Handlers/WindowRegistrationStateMachineTests.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame.Interop; +using InfiniFrame.Window.Interop; namespace InfiniTests.InfiniFrame.Window.Features.WebMessaging.Handlers; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/SendWebMessageAsyncTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/SendWebMessageAsyncTests.cs similarity index 97% rename from tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/SendWebMessageAsyncTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/SendWebMessageAsyncTests.cs index 372bde4ec..5a4b3f65c 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/SendWebMessageAsyncTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/SendWebMessageAsyncTests.cs @@ -2,7 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniTests.Substitutes; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Substitutes; namespace InfiniTests.InfiniFrame.Window.Features.WebMessaging; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/SendWebMessageTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/SendWebMessageTests.cs similarity index 97% rename from tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/SendWebMessageTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/SendWebMessageTests.cs index 544271741..c87ddc735 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/SendWebMessageTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/SendWebMessageTests.cs @@ -2,7 +2,7 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniTests.Substitutes; +using InfiniTests.TestSupport.Substitutes; namespace InfiniTests.InfiniFrame.Window.Features.WebMessaging; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/WebMessageContextTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/WebMessageContextTests.cs similarity index 92% rename from tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/WebMessageContextTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/WebMessageContextTests.cs index 2f2741d2f..73e747cce 100644 --- a/tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/WebMessageContextTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/WebMessageContextTests.cs @@ -2,9 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using System.Text.Json; -using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; -using InfiniTests.Substitutes; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Events; +using InfiniTests.TestSupport.Substitutes; using Microsoft.Extensions.Logging.Abstractions; namespace InfiniTests.InfiniFrame.Window.Features.WebMessaging; @@ -23,7 +23,7 @@ public async Task OnWebMessageReceived_WithOrigin_PublishesOriginViaEventPayload var eventsStore = new InfiniFrameEventsStore(); var events = new InfiniFrameEvents(eventsStore, NullLogger.Instance); var window = new RecordingInfiniFrameWindowSubstitute(); - var nativeParameters = default(InfiniFrameNativeParameters); + var nativeParameters = default(InfiniFrameNativeWindowParameters); events.AssignToNativeParameters(ref nativeParameters); events.AssignToWindow(window.Window); @@ -45,7 +45,7 @@ public async Task OnWebMessageReceived_WithBlazorWebViewMessage_PublishesRawMess var eventsStore = new InfiniFrameEventsStore(); var events = new InfiniFrameEvents(eventsStore, NullLogger.Instance); var window = new RecordingInfiniFrameWindowSubstitute(); - var nativeParameters = default(InfiniFrameNativeParameters); + var nativeParameters = default(InfiniFrameNativeWindowParameters); events.AssignToNativeParameters(ref nativeParameters); events.AssignToWindow(window.Window); diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/WindowFeatureParityTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/WindowFeatureParityTests.cs similarity index 100% rename from tests/InfiniTests.InfiniFrame/Window/Features/WebMessaging/WindowFeatureParityTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/Features/WebMessaging/WindowFeatureParityTests.cs diff --git a/tests/InfiniTests.InfiniFrame/Window/InfiniFrameWindowBuilderConfigurationTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/InfiniFrameWindowBuilderConfigurationTests.cs similarity index 93% rename from tests/InfiniTests.InfiniFrame/Window/InfiniFrameWindowBuilderConfigurationTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/InfiniFrameWindowBuilderConfigurationTests.cs index 39c6b37bb..708b3fefe 100644 --- a/tests/InfiniTests.InfiniFrame/Window/InfiniFrameWindowBuilderConfigurationTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/InfiniFrameWindowBuilderConfigurationTests.cs @@ -2,7 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; +using InfiniTests.TestSupport; namespace InfiniTests.InfiniFrame.Window; // --------------------------------------------------------------------------------------------------------------------- @@ -39,7 +41,7 @@ public async Task ChildWindows_ShouldBeEmptyByDefault(CancellationToken ct = def public async Task ApplyToNativeParameters_ShouldNotThrow(CancellationToken ct = default) { // Arrange var config = new InfiniFrameWindowBuilderConfiguration(); - var parameters = new InfiniFrameNativeParameters(); + var parameters = new InfiniFrameNativeWindowParameters(); // Act config.ApplyToNativeParameters(ref parameters); diff --git a/tests/InfiniTests.InfiniFrame/Window/InfiniFrameWindowBuilderFeaturesTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/InfiniFrameWindowBuilderFeaturesTests.cs similarity index 93% rename from tests/InfiniTests.InfiniFrame/Window/InfiniFrameWindowBuilderFeaturesTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/InfiniFrameWindowBuilderFeaturesTests.cs index 18d12345c..74448461a 100644 --- a/tests/InfiniTests.InfiniFrame/Window/InfiniFrameWindowBuilderFeaturesTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/InfiniFrameWindowBuilderFeaturesTests.cs @@ -1,8 +1,8 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window.Builder; namespace InfiniTests.InfiniFrame.Window; // --------------------------------------------------------------------------------------------------------------------- @@ -37,7 +37,7 @@ public async Task AllFeatures_ShouldBeInitialized(CancellationToken ct = default public async Task ApplyToNativeParameters_ShouldNotThrow(CancellationToken ct = default) { // Arrange var features = new InfiniFrameWindowBuilderFeatures(); - var parameters = new InfiniFrameNativeParameters(); + var parameters = new InfiniFrameNativeWindowParameters(); // Act features.ApplyToNativeParameters(ref parameters); diff --git a/tests/InfiniTests.InfiniFrame.Window/Window/InfiniFrameWindowBuilderTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/InfiniFrameWindowBuilderTests.cs new file mode 100644 index 000000000..49f6cb363 --- /dev/null +++ b/tests/InfiniTests.InfiniFrame.Window/Window/InfiniFrameWindowBuilderTests.cs @@ -0,0 +1,21 @@ +using FluentValidation; +using InfiniFrame.Window; +using InfiniFrame.Window.Builder; +using Microsoft.Extensions.DependencyInjection; + +namespace InfiniTests.InfiniFrame.Window; + +public sealed class InfiniFrameWindowBuilderTests { + [Test] + public async Task Build_InvalidParametersAreRejectedBeforeWindowResolution(CancellationToken ct = default) { + var services = new ServiceCollection(); + services.AddLogging().AddInfiniFrame(); + services.AddTransient(_ => + throw new InvalidOperationException("The window should not be resolved before validation.")); + await using ServiceProvider provider = services.BuildServiceProvider(); + var builder = new InfiniFrameWindowBuilder(); + + // ReSharper disable once AccessToDisposedClosure + await Assert.That(() => builder.Build(provider)).Throws(); + } +} diff --git a/tests/InfiniTests.InfiniFrame/Window/InfiniFrameWindowConfigurationTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/InfiniFrameWindowConfigurationTests.cs similarity index 96% rename from tests/InfiniTests.InfiniFrame/Window/InfiniFrameWindowConfigurationTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/InfiniFrameWindowConfigurationTests.cs index 4194ae0ab..064f92843 100644 --- a/tests/InfiniTests.InfiniFrame/Window/InfiniFrameWindowConfigurationTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/InfiniFrameWindowConfigurationTests.cs @@ -2,7 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; +using InfiniFrame.Window; +using InfiniTests.TestSupport; namespace InfiniTests.InfiniFrame.Window; // --------------------------------------------------------------------------------------------------------------------- @@ -79,7 +81,7 @@ public async Task ChildWindowsLock_IsNotNull(CancellationToken ct = default) { public async Task AssignNativeParameters_SetsStartupParameters(CancellationToken ct = default) { // Arrange var config = new InfiniFrameWindowConfiguration(); - var parameters = new InfiniFrameNativeParameters { + var parameters = new InfiniFrameNativeWindowParameters { Title = "Test Window", Width = 800, Height = 600 diff --git a/tests/InfiniTests.InfiniFrame/Window/InfiniFrameWindowFeaturesTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/InfiniFrameWindowFeaturesTests.cs similarity index 99% rename from tests/InfiniTests.InfiniFrame/Window/InfiniFrameWindowFeaturesTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/InfiniFrameWindowFeaturesTests.cs index 3db985386..c01005d76 100644 --- a/tests/InfiniTests.InfiniFrame/Window/InfiniFrameWindowFeaturesTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/InfiniFrameWindowFeaturesTests.cs @@ -2,6 +2,8 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniFrame.Window; +using InfiniTests.TestSupport; namespace InfiniTests.InfiniFrame.Window; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/WindowIntegrationTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/WindowIntegrationTests.cs similarity index 98% rename from tests/InfiniTests.InfiniFrame/Window/WindowIntegrationTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/WindowIntegrationTests.cs index c16f082be..120dd3517 100644 --- a/tests/InfiniTests.InfiniFrame/Window/WindowIntegrationTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/WindowIntegrationTests.cs @@ -2,6 +2,9 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Utilities; namespace InfiniTests.InfiniFrame.Window; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/WindowTests.cs b/tests/InfiniTests.InfiniFrame.Window/Window/WindowTests.cs similarity index 98% rename from tests/InfiniTests.InfiniFrame/Window/WindowTests.cs rename to tests/InfiniTests.InfiniFrame.Window/Window/WindowTests.cs index 6d9e5df8b..4353dd202 100644 --- a/tests/InfiniTests.InfiniFrame/Window/WindowTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/Window/WindowTests.cs @@ -3,6 +3,9 @@ // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; using InfiniFrame.NativeBridge.Handles; +using InfiniFrame.Window; +using InfiniTests.TestSupport; +using InfiniTests.TestSupport.Attributes; namespace InfiniTests.InfiniFrame.Window; // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/WindowReadyRegistrationStateTests.cs b/tests/InfiniTests.InfiniFrame.Window/WindowReadyRegistrationStateTests.cs similarity index 96% rename from tests/InfiniTests.InfiniFrame/WindowReadyRegistrationStateTests.cs rename to tests/InfiniTests.InfiniFrame.Window/WindowReadyRegistrationStateTests.cs index 906728843..b2c5d5f25 100644 --- a/tests/InfiniTests.InfiniFrame/WindowReadyRegistrationStateTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/WindowReadyRegistrationStateTests.cs @@ -1,9 +1,9 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame.Interop; +using InfiniFrame.Window.Interop; -namespace InfiniTests.InfiniFrame; +namespace InfiniTests.InfiniFrame.Window; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/WindowRegistrationHandshakeStateTests.cs b/tests/InfiniTests.InfiniFrame.Window/WindowRegistrationHandshakeStateTests.cs similarity index 96% rename from tests/InfiniTests.InfiniFrame/WindowRegistrationHandshakeStateTests.cs rename to tests/InfiniTests.InfiniFrame.Window/WindowRegistrationHandshakeStateTests.cs index a95d350f4..566c71a5a 100644 --- a/tests/InfiniTests.InfiniFrame/WindowRegistrationHandshakeStateTests.cs +++ b/tests/InfiniTests.InfiniFrame.Window/WindowRegistrationHandshakeStateTests.cs @@ -1,9 +1,9 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniFrame.Interop; +using InfiniFrame.Window.Interop; -namespace InfiniTests.InfiniFrame; +namespace InfiniTests.InfiniFrame.Window; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Browser/Win32SetWebView2PathTests.cs b/tests/InfiniTests.InfiniFrame/Window/Features/Browser/Win32SetWebView2PathTests.cs deleted file mode 100644 index 989c707af..000000000 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Browser/Win32SetWebView2PathTests.cs +++ /dev/null @@ -1,171 +0,0 @@ -// --------------------------------------------------------------------------------------------------------------------- -// Imports -// --------------------------------------------------------------------------------------------------------------------- -using System.Diagnostics; -using System.Runtime.Versioning; -using System.Text.Json; -using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; - -namespace InfiniTests.InfiniFrame.Window.Features.Browser; -// --------------------------------------------------------------------------------------------------------------------- -// Code -// --------------------------------------------------------------------------------------------------------------------- -[OnlyRunOnWindows] -public class Win32SetWebView2PathTests { - private static readonly HttpClient Client = new() { - Timeout = TimeSpan.FromMilliseconds(500) - }; - - // ----------------------------------------------------------------------------------------------------------------- - // Methods - // ----------------------------------------------------------------------------------------------------------------- - [Test] - public async Task AtBuilderStage_DirectAssignment_PassesPathToNativeParameters(CancellationToken ct) { - // Arrange - var builder = InfiniFrameWindowBuilder.Create(); - const string path = "C:\\WebView2Runtime"; - - // Act - builder.Features.Browser.SetWebView2RuntimePath(path); - InfiniFrameNativeParameters parameters = builder.CollectNativeParameters(); - - // Assert - await Assert.That(builder.Features.Browser.WebView2RuntimePath).IsEqualTo(path); - await Assert.That(parameters.WebView2RuntimePath).IsEqualTo(path); - } - - [Test] - public async Task AtBuilderStage_ExtensionAssignment_ReturnsBuilderAndPassesPathToNativeParameters(CancellationToken ct) { - // Arrange - var builder = InfiniFrameWindowBuilder.Create(); - const string path = "C:\\WebView2Runtime"; - - // Act - IInfiniFrameWindowBuilder returnedBuilder = builder.SetWebView2RuntimePath(path); - InfiniFrameNativeParameters parameters = builder.CollectNativeParameters(); - - // Assert - await Assert.That(returnedBuilder).IsSameReferenceAs(builder); - await Assert.That(parameters.WebView2RuntimePath).IsEqualTo(path); - } - - [Test] - [OnlyRunOnWindowsX64] - [NotInParallelInfiniTests] - [Timeout(300_000)] - public async Task AtWindowStage_FixedRuntimePath_StartsTheConfiguredFixedVersionRuntime(CancellationToken ct) { - if (!OperatingSystem.IsWindows()) { - Skip.Test("This test is only run on Windows."); - return; - } - - string runtimePath = await GetOrProvisionFixedRuntimePath(ct); - await Assert.That(File.Exists(Path.Join(runtimePath, "msedgewebview2.exe"))).IsTrue(); - - int port = GetAvailableLoopbackPort(); - using InfiniFrameTestWindow windowUtility = CreateWindowWithFixedRuntime(runtimePath, port, ct); - - string? browserVersion = await WaitForBrowserVersion(port, ct); - - // Verify the browser started successfully with a non-empty version string. - // The exact version may vary depending on which runtime is resolved, so we - // only assert that a version was reported — the key invariant is that - // SetWebView2RuntimePath caused the window to use the specified runtime path. - await Assert.That(browserVersion).IsNotNull(); - await Assert.That(browserVersion).IsNotEmpty(); - } - - private static async Task GetOrProvisionFixedRuntimePath(CancellationToken ct) { - string? configuredPath = Environment.GetEnvironmentVariable("INFINIFRAME_TEST_WEBVIEW2_RUNTIME_PATH"); - if (!string.IsNullOrWhiteSpace(configuredPath) && File.Exists(Path.Join(configuredPath, "msedgewebview2.exe"))) { - return configuredPath; - } - - string scriptPath = FindRepositoryFile("tests", "ensure-webview2-fixed-runtime.ps1"); - return await Task.Run(function: () => RunProvisioningScript(scriptPath), ct); - } - - private static string RunProvisioningScript(string scriptPath) { - using var provisioningLock = new Mutex(false, "InfiniFrame.WebView2FixedRuntimeProvisioning"); - if (!provisioningLock.WaitOne(TimeSpan.FromMinutes(4))) { - throw new TimeoutException("Timed out waiting to provision the WebView2 fixed runtime."); - } - - try { - var startInfo = new ProcessStartInfo("pwsh") { - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true - }; - startInfo.ArgumentList.Add("-NoProfile"); - startInfo.ArgumentList.Add("-ExecutionPolicy"); - startInfo.ArgumentList.Add("Bypass"); - startInfo.ArgumentList.Add("-File"); - startInfo.ArgumentList.Add(scriptPath); - - using Process process = Process.Start(startInfo) - ?? throw new InvalidOperationException("Could not start PowerShell to provision the WebView2 fixed runtime."); - string standardOutput = process.StandardOutput.ReadToEnd(); - string standardError = process.StandardError.ReadToEnd(); - process.WaitForExit(); - - if (process.ExitCode != 0) { - throw new InvalidOperationException($"WebView2 fixed runtime provisioning failed: {standardError}"); - } - - string runtimePath = standardOutput.Trim(); - if (!File.Exists(Path.Join(runtimePath, "msedgewebview2.exe"))) { - throw new InvalidOperationException("WebView2 fixed runtime provisioning returned an invalid runtime path."); - } - - return runtimePath; - } - finally { - provisioningLock.ReleaseMutex(); - } - } - - private static string FindRepositoryFile(params string[] relativePath) { - foreach (string startPath in new[] { AppContext.BaseDirectory, Environment.CurrentDirectory }) { - for (DirectoryInfo? directory = new(startPath); directory is not null; directory = directory.Parent) { - string candidate = Path.Join([directory.FullName, .. relativePath]); - if (File.Exists(candidate)) return candidate; - } - } - - throw new FileNotFoundException("Could not locate the WebView2 fixed runtime provisioning script."); - } - - [SupportedOSPlatform("windows")] - private static InfiniFrameTestWindow CreateWindowWithFixedRuntime(string runtimePath, int port, CancellationToken ct) - => InfiniFrameTestWindow.Create(builder: builder => builder - .SetWebView2RuntimePath(runtimePath) - .SetRemoteDebuggingPort(port), - ct - ); - - private static int GetAvailableLoopbackPort() => PortUtils.GetOpenPortValue(); - - private static async Task WaitForBrowserVersion(int port, CancellationToken ct) { - DateTime timeoutAt = DateTime.UtcNow.AddSeconds(15); - - while (DateTime.UtcNow < timeoutAt) { - try { - using HttpResponseMessage response = await Client.GetAsync($"http://127.0.0.1:{port}/json/version", ct); - if (response.IsSuccessStatusCode) { - using JsonDocument document = JsonDocument.Parse(await response.Content.ReadAsStringAsync(ct)); - if (document.RootElement.TryGetProperty("Browser", out JsonElement browser)) return browser.GetString(); - } - } - catch (HttpRequestException) { - } - catch (TaskCanceledException) when (!ct.IsCancellationRequested) { - } - - await Task.Delay(200, ct); - } - - return null; - } -} diff --git a/tests/InfiniTests.InfiniFrame/Window/Features/Decorations/WindowsAppUserModelIdTests.cs b/tests/InfiniTests.InfiniFrame/Window/Features/Decorations/WindowsAppUserModelIdTests.cs deleted file mode 100644 index edc484ad7..000000000 --- a/tests/InfiniTests.InfiniFrame/Window/Features/Decorations/WindowsAppUserModelIdTests.cs +++ /dev/null @@ -1,61 +0,0 @@ -// --------------------------------------------------------------------------------------------------------------------- -// Imports -// --------------------------------------------------------------------------------------------------------------------- -using System.Runtime.InteropServices; -using InfiniFrame; -using InfiniFrame.NativeBridge.Parameters; -using InfiniTests.Native; - -namespace InfiniTests.InfiniFrame.Window.Features.Decorations; -// --------------------------------------------------------------------------------------------------------------------- -// Code -// --------------------------------------------------------------------------------------------------------------------- -public sealed class WindowsAppUserModelIdTests { - [Test] - public async Task DirectAssignment_PassesValueToNativeParameters() { - // Arrange - var builder = InfiniFrameWindowBuilder.Create(); - const string value = "InfiniLore.InfiniFrame.Tests"; - - // Act - builder.Features.Decorations.SetWindowsAppUserModelId(value); - InfiniFrameNativeParameters parameters = builder.CollectNativeParameters(); - - // Assert - await Assert.That(builder.Features.Decorations.WindowsAppUserModelId).IsEqualTo(value); - await Assert.That(parameters.WindowsAppUserModelId).IsEqualTo(value); - } - - [Test] - public async Task ExtensionAssignment_ReturnsSameBuilderAndPassesValueToNativeParameters() { - // Arrange - var builder = InfiniFrameWindowBuilder.Create(); - const string value = "InfiniLore.InfiniFrame.Tests"; - - // Act - IInfiniFrameWindowBuilder returnedBuilder = builder.SetWindowsAppUserModelId(value); - InfiniFrameNativeParameters parameters = builder.CollectNativeParameters(); - - // Assert - await Assert.That(returnedBuilder).IsSameReferenceAs(builder); - await Assert.That(parameters.WindowsAppUserModelId).IsEqualTo(value); - } - - [Test] - [OnlyRunOnWindowsX64] - [NotInParallelInfiniTests] - public async Task WindowCreation_AssignsExplicitProcessIdentity(CancellationToken ct) { - const string value = "InfiniLore.InfiniFrame.Tests"; - - using var window = InfiniFrameTestWindow.Create(builder: builder => builder.SetWindowsAppUserModelId(value), ct); - - int result = WindowsNative.GetCurrentProcessAppUserModelId(out IntPtr appUserModelId); - try { - await Assert.That(result).IsEqualTo(0); - await Assert.That(Marshal.PtrToStringUni(appUserModelId)).IsEqualTo(value); - } - finally { - Marshal.FreeCoTaskMem(appUserModelId); - } - } -} diff --git a/tests/InfiniTests/Attributes/DefaultInfiniTestsTimeoutAttribute.cs b/tests/InfiniTests.TestSupport/Attributes/DefaultInfiniTestsTimeoutAttribute.cs similarity index 93% rename from tests/InfiniTests/Attributes/DefaultInfiniTestsTimeoutAttribute.cs rename to tests/InfiniTests.TestSupport/Attributes/DefaultInfiniTestsTimeoutAttribute.cs index 53714b70a..0072d515d 100644 --- a/tests/InfiniTests/Attributes/DefaultInfiniTestsTimeoutAttribute.cs +++ b/tests/InfiniTests.TestSupport/Attributes/DefaultInfiniTestsTimeoutAttribute.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -namespace InfiniTests; +namespace InfiniTests.TestSupport.Attributes; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests/Attributes/MacOsMainThreadTestExecutorAttribute.cs b/tests/InfiniTests.TestSupport/Attributes/MacOsMainThreadTestExecutorAttribute.cs similarity index 95% rename from tests/InfiniTests/Attributes/MacOsMainThreadTestExecutorAttribute.cs rename to tests/InfiniTests.TestSupport/Attributes/MacOsMainThreadTestExecutorAttribute.cs index 28c17545f..160182480 100644 --- a/tests/InfiniTests/Attributes/MacOsMainThreadTestExecutorAttribute.cs +++ b/tests/InfiniTests.TestSupport/Attributes/MacOsMainThreadTestExecutorAttribute.cs @@ -3,7 +3,7 @@ // --------------------------------------------------------------------------------------------------------------------- using TUnit.Core.Interfaces; -namespace InfiniTests; +namespace InfiniTests.TestSupport.Attributes; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests/Attributes/NotInParallelInfiniAutomationTestsAttribute.cs b/tests/InfiniTests.TestSupport/Attributes/NotInParallelInfiniAutomationTestsAttribute.cs similarity index 93% rename from tests/InfiniTests/Attributes/NotInParallelInfiniAutomationTestsAttribute.cs rename to tests/InfiniTests.TestSupport/Attributes/NotInParallelInfiniAutomationTestsAttribute.cs index 429d82602..37d71e38d 100644 --- a/tests/InfiniTests/Attributes/NotInParallelInfiniAutomationTestsAttribute.cs +++ b/tests/InfiniTests.TestSupport/Attributes/NotInParallelInfiniAutomationTestsAttribute.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -namespace InfiniTests; +namespace InfiniTests.TestSupport.Attributes; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests/Attributes/NotInParallelInfiniTestsAttribute.cs b/tests/InfiniTests.TestSupport/Attributes/NotInParallelInfiniTestsAttribute.cs similarity index 92% rename from tests/InfiniTests/Attributes/NotInParallelInfiniTestsAttribute.cs rename to tests/InfiniTests.TestSupport/Attributes/NotInParallelInfiniTestsAttribute.cs index ab95d9584..bede41c66 100644 --- a/tests/InfiniTests/Attributes/NotInParallelInfiniTestsAttribute.cs +++ b/tests/InfiniTests.TestSupport/Attributes/NotInParallelInfiniTestsAttribute.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -namespace InfiniTests; +namespace InfiniTests.TestSupport.Attributes; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests/Attributes/OnlyRunOnLinuxAttribute.cs b/tests/InfiniTests.TestSupport/Attributes/OnlyRunOnLinuxAttribute.cs similarity index 94% rename from tests/InfiniTests/Attributes/OnlyRunOnLinuxAttribute.cs rename to tests/InfiniTests.TestSupport/Attributes/OnlyRunOnLinuxAttribute.cs index 57f077982..ea67b90d9 100644 --- a/tests/InfiniTests/Attributes/OnlyRunOnLinuxAttribute.cs +++ b/tests/InfiniTests.TestSupport/Attributes/OnlyRunOnLinuxAttribute.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -namespace InfiniTests; +namespace InfiniTests.TestSupport.Attributes; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests/Attributes/OnlyRunOnMacOsAttribute.cs b/tests/InfiniTests.TestSupport/Attributes/OnlyRunOnMacOsAttribute.cs similarity index 94% rename from tests/InfiniTests/Attributes/OnlyRunOnMacOsAttribute.cs rename to tests/InfiniTests.TestSupport/Attributes/OnlyRunOnMacOsAttribute.cs index e63b08f0e..77a1612e8 100644 --- a/tests/InfiniTests/Attributes/OnlyRunOnMacOsAttribute.cs +++ b/tests/InfiniTests.TestSupport/Attributes/OnlyRunOnMacOsAttribute.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -namespace InfiniTests; +namespace InfiniTests.TestSupport.Attributes; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests/Attributes/OnlyRunOnWindowsAttribute.cs b/tests/InfiniTests.TestSupport/Attributes/OnlyRunOnWindowsAttribute.cs similarity index 94% rename from tests/InfiniTests/Attributes/OnlyRunOnWindowsAttribute.cs rename to tests/InfiniTests.TestSupport/Attributes/OnlyRunOnWindowsAttribute.cs index 648d0fb8c..cc4ab3073 100644 --- a/tests/InfiniTests/Attributes/OnlyRunOnWindowsAttribute.cs +++ b/tests/InfiniTests.TestSupport/Attributes/OnlyRunOnWindowsAttribute.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -namespace InfiniTests; +namespace InfiniTests.TestSupport.Attributes; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests/Attributes/OnlyRunOnWindowsX64Attribute.cs b/tests/InfiniTests.TestSupport/Attributes/OnlyRunOnWindowsX64Attribute.cs similarity index 95% rename from tests/InfiniTests/Attributes/OnlyRunOnWindowsX64Attribute.cs rename to tests/InfiniTests.TestSupport/Attributes/OnlyRunOnWindowsX64Attribute.cs index 0094161e1..ca32f3718 100644 --- a/tests/InfiniTests/Attributes/OnlyRunOnWindowsX64Attribute.cs +++ b/tests/InfiniTests.TestSupport/Attributes/OnlyRunOnWindowsX64Attribute.cs @@ -3,7 +3,7 @@ // --------------------------------------------------------------------------------------------------------------------- using System.Runtime.InteropServices; -namespace InfiniTests; +namespace InfiniTests.TestSupport.Attributes; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests/Attributes/SkipOnLinuxAttribute.cs b/tests/InfiniTests.TestSupport/Attributes/SkipOnLinuxAttribute.cs similarity index 94% rename from tests/InfiniTests/Attributes/SkipOnLinuxAttribute.cs rename to tests/InfiniTests.TestSupport/Attributes/SkipOnLinuxAttribute.cs index 21fde0e00..68bc06160 100644 --- a/tests/InfiniTests/Attributes/SkipOnLinuxAttribute.cs +++ b/tests/InfiniTests.TestSupport/Attributes/SkipOnLinuxAttribute.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -namespace InfiniTests; +namespace InfiniTests.TestSupport.Attributes; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests/Attributes/SkipOnMacOsAttribute.cs b/tests/InfiniTests.TestSupport/Attributes/SkipOnMacOsAttribute.cs similarity index 94% rename from tests/InfiniTests/Attributes/SkipOnMacOsAttribute.cs rename to tests/InfiniTests.TestSupport/Attributes/SkipOnMacOsAttribute.cs index 56789f7bf..9e77ba461 100644 --- a/tests/InfiniTests/Attributes/SkipOnMacOsAttribute.cs +++ b/tests/InfiniTests.TestSupport/Attributes/SkipOnMacOsAttribute.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -namespace InfiniTests; +namespace InfiniTests.TestSupport.Attributes; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests/Attributes/SkipOnWindowsArmAttribute.cs b/tests/InfiniTests.TestSupport/Attributes/SkipOnWindowsArmAttribute.cs similarity index 95% rename from tests/InfiniTests/Attributes/SkipOnWindowsArmAttribute.cs rename to tests/InfiniTests.TestSupport/Attributes/SkipOnWindowsArmAttribute.cs index 506a7845f..b2a34a129 100644 --- a/tests/InfiniTests/Attributes/SkipOnWindowsArmAttribute.cs +++ b/tests/InfiniTests.TestSupport/Attributes/SkipOnWindowsArmAttribute.cs @@ -3,7 +3,7 @@ // --------------------------------------------------------------------------------------------------------------------- using System.Runtime.InteropServices; -namespace InfiniTests; +namespace InfiniTests.TestSupport.Attributes; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests/Attributes/SkipOnWindowsAttribute.cs b/tests/InfiniTests.TestSupport/Attributes/SkipOnWindowsAttribute.cs similarity index 94% rename from tests/InfiniTests/Attributes/SkipOnWindowsAttribute.cs rename to tests/InfiniTests.TestSupport/Attributes/SkipOnWindowsAttribute.cs index 0b5572f94..18cec7680 100644 --- a/tests/InfiniTests/Attributes/SkipOnWindowsAttribute.cs +++ b/tests/InfiniTests.TestSupport/Attributes/SkipOnWindowsAttribute.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -namespace InfiniTests; +namespace InfiniTests.TestSupport.Attributes; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests/InfiniFrameTestServer.cs b/tests/InfiniTests.TestSupport/InfiniFrameTestServer.cs similarity index 74% rename from tests/InfiniTests/InfiniFrameTestServer.cs rename to tests/InfiniTests.TestSupport/InfiniFrameTestServer.cs index b3a7e718d..15ba3187e 100644 --- a/tests/InfiniTests/InfiniFrameTestServer.cs +++ b/tests/InfiniTests.TestSupport/InfiniFrameTestServer.cs @@ -2,13 +2,14 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniFrame.Application; using InfiniFrame.Utilities; using InfiniFrame.WebServer; using JetBrains.Annotations; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -namespace InfiniTests; +namespace InfiniTests.TestSupport; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -64,7 +65,7 @@ public async ValueTask DisposeAsync() { } public static InfiniFrameTestServer Create( - Action? appBuilder = null, + Action? appBuilder = null, Action? windowBuilder = null, CancellationToken cancellationToken = default ) { @@ -72,30 +73,29 @@ public static InfiniFrameTestServer Create( TaskCreationOptions.RunContinuationsAsynchronously); var thread = new Thread(() => { try { - InfiniFrameWebApplicationBuilder builder = InfiniFrameWebApplication.CreateBuilder(); - builder.WebApp.WebHost.UseStaticWebAssets(); - - appBuilder?.Invoke(builder.WebApp); - windowBuilder?.Invoke(builder.WindowBuilder); - - InfiniFrameWebApplication app = builder.Build(); - - app.WebApp.UseDefaultFiles(); - app.WebApp.UseStaticFiles(); - - #if !NET8_0 - app.WebApp.MapStaticAssets(); - #endif - - // Exercise the documented WebServer lifecycle in automation tests. Run() starts - // Kestrel before creating the window, and the created callback runs on this STA - // thread immediately before WaitForClose() enters the native message loop. - app.UseAutoServerClose(); - builder.WindowBuilder.EventsStore.WindowCreated.Add(w => { - ready.SetResult((w, app.WebApp)); + InfiniFrameApplicationBuilder builder = InfiniFrameApplication.CreateBuilder(); + builder.WithWindow(windowBuilder ??= static _ => { }); + var webApplicationReady = new TaskCompletionSource( + TaskCreationOptions.RunContinuationsAsynchronously); + + builder.UseWebServer(web => { + appBuilder?.Invoke(web.WebHost); + web.ConfigureWebApplication(webApp => { + webApp.UseDefaultFiles(); + webApp.UseStaticFiles(); + #if !NET8_0 + webApp.MapStaticAssets(); + #endif + webApplicationReady.TrySetResult(webApp); + }); }); - app.Run(); + InfiniFrameApplication application = builder.Build(); + application.WindowCreated += infiniFrameWindow => { + if (webApplicationReady.Task.IsCompletedSuccessfully) + ready.TrySetResult((infiniFrameWindow, webApplicationReady.Task.Result)); + }; + application.Run(); } catch (Exception ex) when (ExceptionsUtility.IsNonFatalException(ex)) { if (!ready.TrySetException(ex)) diff --git a/tests/InfiniTests/InfiniFrameTestWindow.Linux.cs b/tests/InfiniTests.TestSupport/InfiniFrameTestWindow.Linux.cs similarity index 93% rename from tests/InfiniTests/InfiniFrameTestWindow.Linux.cs rename to tests/InfiniTests.TestSupport/InfiniFrameTestWindow.Linux.cs index 4a91df1e5..6182237bd 100644 --- a/tests/InfiniTests/InfiniFrameTestWindow.Linux.cs +++ b/tests/InfiniTests.TestSupport/InfiniFrameTestWindow.Linux.cs @@ -3,9 +3,10 @@ // --------------------------------------------------------------------------------------------------------------------- using System.Runtime.Versioning; using InfiniFrame; +using InfiniFrame.Window.Builder; using JetBrains.Annotations; -namespace InfiniTests; +namespace InfiniTests.TestSupport; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests/InfiniFrameTestWindow.MacOs.cs b/tests/InfiniTests.TestSupport/InfiniFrameTestWindow.MacOs.cs similarity index 93% rename from tests/InfiniTests/InfiniFrameTestWindow.MacOs.cs rename to tests/InfiniTests.TestSupport/InfiniFrameTestWindow.MacOs.cs index d417c935e..948788dfb 100644 --- a/tests/InfiniTests/InfiniFrameTestWindow.MacOs.cs +++ b/tests/InfiniTests.TestSupport/InfiniFrameTestWindow.MacOs.cs @@ -3,9 +3,10 @@ // --------------------------------------------------------------------------------------------------------------------- using System.Runtime.Versioning; using InfiniFrame; +using InfiniFrame.Window.Builder; using JetBrains.Annotations; -namespace InfiniTests; +namespace InfiniTests.TestSupport; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests/InfiniFrameTestWindow.Windows.cs b/tests/InfiniTests.TestSupport/InfiniFrameTestWindow.Windows.cs similarity index 70% rename from tests/InfiniTests/InfiniFrameTestWindow.Windows.cs rename to tests/InfiniTests.TestSupport/InfiniFrameTestWindow.Windows.cs index 15e7875ca..5d4cb3071 100644 --- a/tests/InfiniTests/InfiniFrameTestWindow.Windows.cs +++ b/tests/InfiniTests.TestSupport/InfiniFrameTestWindow.Windows.cs @@ -5,9 +5,10 @@ using InfiniFrame; using InfiniFrame.NativeBridge.Handles; using InfiniFrame.Utilities; +using InfiniFrame.Window.Builder; using JetBrains.Annotations; -namespace InfiniTests; +namespace InfiniTests.TestSupport; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -19,9 +20,17 @@ public sealed partial class InfiniFrameTestWindow { [MustDisposeResource] private static partial InfiniFrameTestWindow CreateWindows(InfiniFrameWindowBuilder windowBuilder) { var windowSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + using var startupMutex = new Mutex(false, "InfiniFrameDesktopStartup"); + try { + startupMutex.WaitOne(); + } + catch (AbandonedMutexException) { + // The previous test process exited during desktop startup. + } - var thread = new Thread(() => { - try { + try { + var thread = new Thread(() => { + try { if (TestWindowTracingEnabled) { Console.Error.WriteLine( $"[InfiniFrameWindowTestUtility] STA thread started managedThreadId={Environment.CurrentManagedThreadId} apt={Thread.CurrentThread.GetApartmentState()} pid={Environment.ProcessId}"); @@ -41,20 +50,24 @@ private static partial InfiniFrameTestWindow CreateWindows(InfiniFrameWindowBuil } catch (Exception ex) when (ExceptionsUtility.IsNonFatalException(ex)) { windowSource.TrySetException(ex); - } - }) { - IsBackground = true, - Name = "InfiniFrame Test Window Thread" - }; + } + }) { + IsBackground = true, + Name = "InfiniFrame Test Window Thread" + }; - thread.SetApartmentState(ApartmentState.STA); + thread.SetApartmentState(ApartmentState.STA); - thread.Start(); + thread.Start(); - return new InfiniFrameTestWindow { - BuilderSnapshot = windowBuilder, - Window = windowSource.Task.GetAwaiter().GetResult(), - _windowThread = thread - }; + return new InfiniFrameTestWindow { + BuilderSnapshot = windowBuilder, + Window = windowSource.Task.GetAwaiter().GetResult(), + _windowThread = thread + }; + } + finally { + startupMutex.ReleaseMutex(); + } } } diff --git a/tests/InfiniTests/InfiniFrameTestWindow.cs b/tests/InfiniTests.TestSupport/InfiniFrameTestWindow.cs similarity index 97% rename from tests/InfiniTests/InfiniFrameTestWindow.cs rename to tests/InfiniTests.TestSupport/InfiniFrameTestWindow.cs index a446c104e..071091217 100644 --- a/tests/InfiniTests/InfiniFrameTestWindow.cs +++ b/tests/InfiniTests.TestSupport/InfiniFrameTestWindow.cs @@ -2,9 +2,10 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; +using InfiniFrame.Window.Builder; using JetBrains.Annotations; -namespace InfiniTests; +namespace InfiniTests.TestSupport; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -99,7 +100,7 @@ public static InfiniFrameTestWindow Create(CancellationToken cancellationToken = public static InfiniFrameTestWindow Create(Action? builder = null, CancellationToken ct = default) { ct.ThrowIfCancellationRequested(); - var windowBuilder = InfiniFrameWindowBuilder.Create(); + var windowBuilder = new InfiniFrameWindowBuilder(); windowBuilder .SetIconFile("wwwroot/favicon.ico") .SetStartPageContent(StartString); diff --git a/tests/InfiniTests/InfiniTests.csproj b/tests/InfiniTests.TestSupport/InfiniTests.TestSupport.csproj similarity index 57% rename from tests/InfiniTests/InfiniTests.csproj rename to tests/InfiniTests.TestSupport/InfiniTests.TestSupport.csproj index f14c7ab5b..cbf759491 100644 --- a/tests/InfiniTests/InfiniTests.csproj +++ b/tests/InfiniTests.TestSupport/InfiniTests.TestSupport.csproj @@ -6,7 +6,9 @@ - + + + diff --git a/tests/InfiniTests/InfiniTests.csproj.DotSettings b/tests/InfiniTests.TestSupport/InfiniTests.csproj.DotSettings similarity index 100% rename from tests/InfiniTests/InfiniTests.csproj.DotSettings rename to tests/InfiniTests.TestSupport/InfiniTests.csproj.DotSettings diff --git a/tests/InfiniTests/JsRuntimes/RecordingJsRuntime.cs b/tests/InfiniTests.TestSupport/JsRuntimes/RecordingJsRuntime.cs similarity index 97% rename from tests/InfiniTests/JsRuntimes/RecordingJsRuntime.cs rename to tests/InfiniTests.TestSupport/JsRuntimes/RecordingJsRuntime.cs index 27c1b2dfb..47e35da34 100644 --- a/tests/InfiniTests/JsRuntimes/RecordingJsRuntime.cs +++ b/tests/InfiniTests.TestSupport/JsRuntimes/RecordingJsRuntime.cs @@ -3,7 +3,7 @@ // --------------------------------------------------------------------------------------------------------------------- using Microsoft.JSInterop; -namespace InfiniTests.JsRuntimes; +namespace InfiniTests.TestSupport.JsRuntimes; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests/MacOsMainThreadExecutor.cs b/tests/InfiniTests.TestSupport/MacOsMainThreadExecutor.cs similarity index 98% rename from tests/InfiniTests/MacOsMainThreadExecutor.cs rename to tests/InfiniTests.TestSupport/MacOsMainThreadExecutor.cs index 32d500d4d..22aea7934 100644 --- a/tests/InfiniTests/MacOsMainThreadExecutor.cs +++ b/tests/InfiniTests.TestSupport/MacOsMainThreadExecutor.cs @@ -2,10 +2,11 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using System.Runtime.InteropServices; -using InfiniTests.Native; +using InfiniTests.TestSupport.Attributes; +using InfiniTests.TestSupport.Native; using TUnit.Core.Interfaces; -namespace InfiniTests; +namespace InfiniTests.TestSupport; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests/MockFactory.cs b/tests/InfiniTests.TestSupport/MockFactory.cs similarity index 94% rename from tests/InfiniTests/MockFactory.cs rename to tests/InfiniTests.TestSupport/MockFactory.cs index 91974d5b4..17a6860e7 100644 --- a/tests/InfiniTests/MockFactory.cs +++ b/tests/InfiniTests.TestSupport/MockFactory.cs @@ -5,11 +5,11 @@ using InfiniFrame; using InfiniFrame.BlazorWebView; using InfiniFrame.NativeBridge.Delegates; -using InfiniFrame.NativeBridge.Parameters; +using InfiniFrame.NativeBridge.Parameters.Window; using Microsoft.AspNetCore.Components; using Microsoft.Extensions.Logging; -namespace InfiniTests; +namespace InfiniTests.TestSupport; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- @@ -43,5 +43,5 @@ public static class MockFactory { public static Mock CreateReleaseDelegateMock() => Mock.Of(); public static Mock CreateServiceProviderMock() => Mock.Of(); public static Mock CreateDisposableMock() => Mock.Of(); - public static Mock> CreateValidatorMock() => Mock.Of>(); + public static Mock> CreateValidatorMock() => Mock.Of>(); } diff --git a/tests/InfiniTests/Native/MacOsNative.cs b/tests/InfiniTests.TestSupport/Native/MacOsNative.cs similarity index 97% rename from tests/InfiniTests/Native/MacOsNative.cs rename to tests/InfiniTests.TestSupport/Native/MacOsNative.cs index 3bb32a665..1a3518f75 100644 --- a/tests/InfiniTests/Native/MacOsNative.cs +++ b/tests/InfiniTests.TestSupport/Native/MacOsNative.cs @@ -3,7 +3,7 @@ // --------------------------------------------------------------------------------------------------------------------- using System.Runtime.InteropServices; -namespace InfiniTests.Native; +namespace InfiniTests.TestSupport.Native; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests/Native/WindowsNative.cs b/tests/InfiniTests.TestSupport/Native/WindowsNative.cs similarity index 96% rename from tests/InfiniTests/Native/WindowsNative.cs rename to tests/InfiniTests.TestSupport/Native/WindowsNative.cs index 65f0f866e..ff38e4c3b 100644 --- a/tests/InfiniTests/Native/WindowsNative.cs +++ b/tests/InfiniTests.TestSupport/Native/WindowsNative.cs @@ -3,7 +3,7 @@ // --------------------------------------------------------------------------------------------------------------------- using System.Runtime.InteropServices; -namespace InfiniTests.Native; +namespace InfiniTests.TestSupport.Native; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests/Substitutes/RecordingInfiniFrameWindowSubstitute.cs b/tests/InfiniTests.TestSupport/Substitutes/RecordingInfiniFrameWindowSubstitute.cs similarity index 97% rename from tests/InfiniTests/Substitutes/RecordingInfiniFrameWindowSubstitute.cs rename to tests/InfiniTests.TestSupport/Substitutes/RecordingInfiniFrameWindowSubstitute.cs index 77a0cfd7f..9c8f88a7e 100644 --- a/tests/InfiniTests/Substitutes/RecordingInfiniFrameWindowSubstitute.cs +++ b/tests/InfiniTests.TestSupport/Substitutes/RecordingInfiniFrameWindowSubstitute.cs @@ -2,10 +2,11 @@ // Imports // --------------------------------------------------------------------------------------------------------------------- using InfiniFrame; -using InfiniFrame.Interop; +using InfiniFrame.Js.Interop; +using InfiniFrame.Window.Events; using Microsoft.Extensions.Logging.Abstractions; -namespace InfiniTests.Substitutes; +namespace InfiniTests.TestSupport.Substitutes; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests/Utilities/PollUtility.cs b/tests/InfiniTests.TestSupport/Utilities/PollUtility.cs similarity index 97% rename from tests/InfiniTests/Utilities/PollUtility.cs rename to tests/InfiniTests.TestSupport/Utilities/PollUtility.cs index aea97bff3..99b89ce17 100644 --- a/tests/InfiniTests/Utilities/PollUtility.cs +++ b/tests/InfiniTests.TestSupport/Utilities/PollUtility.cs @@ -1,7 +1,7 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -namespace InfiniTests; +namespace InfiniTests.TestSupport.Utilities; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/InfiniTests/Utilities/PortUtils.cs b/tests/InfiniTests.TestSupport/Utilities/PortUtils.cs similarity index 98% rename from tests/InfiniTests/Utilities/PortUtils.cs rename to tests/InfiniTests.TestSupport/Utilities/PortUtils.cs index eed4a149a..ed03d9b41 100644 --- a/tests/InfiniTests/Utilities/PortUtils.cs +++ b/tests/InfiniTests.TestSupport/Utilities/PortUtils.cs @@ -5,7 +5,7 @@ using System.Net.Sockets; using System.Runtime.CompilerServices; -namespace InfiniTests; +namespace InfiniTests.TestSupport.Utilities; // --------------------------------------------------------------------------------------------------------------------- // Code // --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/README.md b/tests/README.md index bdafcb8cf..009fe03f9 100644 --- a/tests/README.md +++ b/tests/README.md @@ -8,8 +8,9 @@ This directory contains the unit, integration, and automation test suites for In | Project | Description | |------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------| -| `InfiniTests/` | Core unit tests using TUnit and TUnit.Mocks. Contains platform-specific window abstractions, mock factories, native interop tests, and JS runtime tests. | -| `InfiniTests.InfiniFrame/` | Core package unit tests | +| `InfiniTests.TestSupport/` | Shared fixtures, platform helpers, mocks, and test-host infrastructure | +| `InfiniTests.InfiniFrame.Application/` | Application lifecycle, registration, and run-loop tests | +| `InfiniTests.InfiniFrame.Window/` | Native window, feature, event, and lifecycle tests | | `InfiniTests.InfiniFrame.Blazor/` | Blazor component tests | | `InfiniTests.InfiniFrame.BlazorWebView/` | BlazorWebView integration tests | | `InfiniTests.InfiniFrame.Js/` | JavaScript interop tests | @@ -51,13 +52,13 @@ Python unit tests for repository scripts (in `tests/scripts/`): ### Run All Tests ```bash -dotnet test tests/InfiniTests.InfiniFrame.slnf +dotnet test InfiniFrame.GitHubActions.Testing.slnf ``` ### Run Specific Test Project ```bash -dotnet test tests/InfiniTests.InfiniFrame/InfiniTests.InfiniFrame.csproj +dotnet test tests/InfiniTests.InfiniFrame.Window/InfiniTests.InfiniFrame.Window.csproj ``` ### Run with Filter diff --git a/tests/TestHost/MacOsTestingPlatformEntryPoint.cs b/tests/TestHost/MacOsTestingPlatformEntryPoint.cs index e0b0fd90a..ef4dbe21d 100644 --- a/tests/TestHost/MacOsTestingPlatformEntryPoint.cs +++ b/tests/TestHost/MacOsTestingPlatformEntryPoint.cs @@ -1,7 +1,6 @@ // --------------------------------------------------------------------------------------------------------------------- // Imports // --------------------------------------------------------------------------------------------------------------------- -using InfiniTests.Native; using Microsoft.Testing.Platform.Builder; using System.Reflection; using System.Runtime.InteropServices;