-
Notifications
You must be signed in to change notification settings - Fork 882
Add BrowserUserDataMode to separate user data dir from profile #16457
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 24 commits
Commits
Show all changes
36 commits
Select commit
Hold shift + click to select a range
016dbfb
Add BrowserUserDataMode to separate user data dir from profile
davidfowl 1cdce4e
Make Shared the default user data mode and document singleton caveat
davidfowl b00e33a
Add Target lifecycle and Inspector.detached protocol parsing
davidfowl 1fb3f58
Add IBrowserHost abstraction with ownership distinction
davidfowl 71f2f07
Apply review feedback to BrowserHostIdentity and document Target even…
davidfowl e5d4fc4
Harden browser logs foundation for shared browser adoption
davidfowl e6a1f56
Support shared browser host adoption for browser logs
davidfowl bbcaa81
Improve browser logs diagnostics
davidfowl 11d5f9b
Update browser logs codegen snapshots
davidfowl 64169bf
Add browser logs component tests
davidfowl ac96567
Reuse browser endpoint probe HttpClient
davidfowl ef381b0
Use typed browser endpoint probe response
davidfowl 3f259b1
Remove redundant adopted browser disposal guard
davidfowl fba86ac
Document browser host registry decisions
davidfowl ba981ec
Add browser observation notes to registry comments
davidfowl 46edb61
Document browser log runtime behavior
davidfowl 07c9794
Name browser logs timeout values
davidfowl c812643
Rename browser logs CDP transport types
davidfowl adb4bbe
Document browser endpoint discovery flow
davidfowl 8b664f4
Link Chromium singleton documentation
davidfowl 57baf51
Clarify browser page session model
davidfowl ef4423d
Dispose browser host registry lock
davidfowl cb4bada
Move browser logs configuration resolution
davidfowl 960ef62
Clean up browser configuration state
davidfowl d39fc3a
Move Chromium browser resolution helpers
davidfowl 5a50201
Document browser logs JSON formats
davidfowl 57cea6f
Localize browser logs failure messages
davidfowl ce82bc8
Move browser launch helpers out of running session
davidfowl 37573bb
Clean up browser logs helper ownership
davidfowl 7de3f07
Address browser logs review feedback
davidfowl 16bf244
Add browser logs baseline coverage
davidfowl 92ae583
Test browser logs CDP connection
davidfowl 1e0574b
Test browser logs running session glue
davidfowl faa9cbb
Test browser page session reconnect
davidfowl 2a0694b
Redefine Shared/Isolated as Aspire-managed persistent profiles
davidfowl fb3fd5c
Address PR review feedback
davidfowl File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,150 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| using Microsoft.Extensions.Configuration; | ||
|
|
||
| namespace Aspire.Hosting; | ||
|
|
||
| /// <summary> | ||
| /// Selects the Chromium user data directory used by tracked browser sessions. | ||
| /// </summary> | ||
| public enum BrowserUserDataMode | ||
| { | ||
| /// <summary> | ||
| /// Use the browser's real user data directory so the tracked session behaves like a persistent browser context | ||
| /// with real cookies, sessions, extensions, and profile selection. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// NOTE: Aspire can adopt a shared browser only when it previously launched that browser with remote debugging | ||
| /// enabled. If a normal non-debuggable browser is already using the selected user data directory, the tracked | ||
| /// session fails with guidance instead of opening a second browser against the same profile store. Google Chrome | ||
| /// also blocks remote debugging against its default user data directory; use Microsoft Edge or <see cref="Isolated"/> | ||
| /// mode when Chrome is selected. | ||
| /// </remarks> | ||
| Shared, | ||
|
|
||
| /// <summary> | ||
| /// Launch the tracked browser against a temporary user data directory, like a disposable persistent browser | ||
| /// context, so the session starts from clean state and does not affect the user's normal browser profiles. | ||
| /// </summary> | ||
| Isolated, | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Resolved browser configuration used for one tracked browser session. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// Resolution keeps "which browser/profile did the caller ask for?" separate from "which user data directory | ||
| /// does that imply?". The later user-data-directory decision belongs to <see cref="BrowserHostRegistry"/>, where | ||
| /// the resolved browser executable path is available. | ||
| /// </remarks> | ||
| internal readonly record struct BrowserConfiguration(string Browser, string? Profile, BrowserUserDataMode UserDataMode) | ||
| { | ||
| /// <summary> | ||
| /// The default mode matches a normal browser launch by using the browser's real user data directory. | ||
| /// </summary> | ||
| internal const BrowserUserDataMode DefaultUserDataMode = BrowserUserDataMode.Shared; | ||
|
|
||
| /// <summary> | ||
| /// Resolves explicit method arguments, resource-scoped configuration, global configuration, and defaults. | ||
| /// </summary> | ||
| internal static BrowserConfiguration Resolve( | ||
| IConfiguration configuration, | ||
| string resourceName, | ||
| BrowserConfigurationOverrides overrides) | ||
| { | ||
| ArgumentNullException.ThrowIfNull(configuration); | ||
| ArgumentException.ThrowIfNullOrWhiteSpace(resourceName); | ||
|
|
||
| var browserLogsSection = configuration.GetSection(BrowserLogsBuilderExtensions.BrowserLogsConfigurationSectionName); | ||
| var resourceSection = browserLogsSection.GetSection(resourceName); | ||
|
|
||
| // Resolution order is explicit argument -> resource-specific config -> global browser-log config -> default. | ||
| // Resolve user-data mode before browser so the browser default can prefer Edge for shared state and Chrome for | ||
| // disposable isolated state. | ||
| var resolvedProfile = overrides.Profile | ||
| ?? resourceSection[BrowserLogsBuilderExtensions.ProfileConfigurationKey] | ||
| ?? browserLogsSection[BrowserLogsBuilderExtensions.ProfileConfigurationKey]; | ||
| var resolvedUserDataMode = overrides.UserDataMode | ||
| ?? ParseUserDataMode(resourceSection[BrowserLogsBuilderExtensions.UserDataModeConfigurationKey]) | ||
| ?? ParseUserDataMode(browserLogsSection[BrowserLogsBuilderExtensions.UserDataModeConfigurationKey]) | ||
| ?? DefaultUserDataMode; | ||
| var resolvedBrowser = overrides.Browser | ||
| ?? resourceSection[BrowserLogsBuilderExtensions.BrowserConfigurationKey] | ||
| ?? browserLogsSection[BrowserLogsBuilderExtensions.BrowserConfigurationKey] | ||
| ?? GetDefaultBrowser(resolvedUserDataMode); | ||
|
|
||
| if (string.IsNullOrWhiteSpace(resolvedBrowser)) | ||
| { | ||
| throw new InvalidOperationException("Tracked browser configuration resolved an empty browser value."); | ||
| } | ||
|
|
||
| if (resolvedProfile is not null && string.IsNullOrWhiteSpace(resolvedProfile)) | ||
| { | ||
| throw new InvalidOperationException("Tracked browser configuration resolved an empty profile value."); | ||
| } | ||
|
|
||
| if (resolvedUserDataMode == BrowserUserDataMode.Isolated && resolvedProfile is not null) | ||
| { | ||
| throw new InvalidOperationException( | ||
| $"Tracked browser configuration set '{BrowserLogsBuilderExtensions.ProfileConfigurationKey}' to '{resolvedProfile}' while '{BrowserLogsBuilderExtensions.UserDataModeConfigurationKey}' is '{BrowserUserDataMode.Isolated}'. " + | ||
| $"Profiles can only be selected when '{BrowserLogsBuilderExtensions.UserDataModeConfigurationKey}' is '{BrowserUserDataMode.Shared}'."); | ||
| } | ||
|
|
||
| return new BrowserConfiguration(resolvedBrowser, resolvedProfile, resolvedUserDataMode); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Selects the default browser for the default user data mode. | ||
| /// </summary> | ||
| internal static string GetDefaultBrowser(Func<string, string?> resolveBrowserExecutable) => | ||
| GetDefaultBrowser(DefaultUserDataMode, resolveBrowserExecutable); | ||
|
|
||
| /// <summary> | ||
| /// Selects the default browser for the effective user data mode. | ||
| /// </summary> | ||
| internal static string GetDefaultBrowser(BrowserUserDataMode userDataMode, Func<string, string?> resolveBrowserExecutable) | ||
| { | ||
| if (userDataMode == BrowserUserDataMode.Shared && | ||
| resolveBrowserExecutable("msedge") is not null) | ||
| { | ||
| return "msedge"; | ||
| } | ||
|
|
||
| if (resolveBrowserExecutable("chrome") is not null) | ||
| { | ||
| return "chrome"; | ||
| } | ||
|
|
||
| if (resolveBrowserExecutable("msedge") is not null) | ||
| { | ||
| return "msedge"; | ||
| } | ||
|
|
||
| return "chrome"; | ||
| } | ||
|
|
||
| private static BrowserUserDataMode? ParseUserDataMode(string? value) | ||
| { | ||
| if (string.IsNullOrWhiteSpace(value)) | ||
| { | ||
| return null; | ||
| } | ||
|
|
||
| if (Enum.TryParse<BrowserUserDataMode>(value, ignoreCase: true, out var parsed)) | ||
| { | ||
| return parsed; | ||
| } | ||
|
|
||
| throw new InvalidOperationException( | ||
| $"Tracked browser configuration value '{value}' is not a valid '{BrowserLogsBuilderExtensions.UserDataModeConfigurationKey}'. Expected '{BrowserUserDataMode.Shared}' or '{BrowserUserDataMode.Isolated}'."); | ||
| } | ||
|
|
||
| private static string GetDefaultBrowser(BrowserUserDataMode userDataMode) => | ||
| GetDefaultBrowser(userDataMode, BrowserLogsRunningSession.TryResolveBrowserExecutable); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Explicit browser configuration values supplied by the resource builder. | ||
| /// </summary> | ||
| internal readonly record struct BrowserConfigurationOverrides(string? Browser, string? Profile, BrowserUserDataMode? UserDataMode); | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.