[Windows] Fix WebView Does Not Inherit App Theme#35037
[Windows] Fix WebView Does Not Inherit App Theme#35037devanathan-vaithiyanathan wants to merge 8 commits intodotnet:mainfrom
Conversation
|
🚀 Dogfood this PR with:
curl -fsSL https://github.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 35037Or
iex "& { $(irm https://github.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 35037" |
|
/azp run maui-pr-uitests, maui-pr-devicetests |
|
Azure Pipelines successfully started running 2 pipeline(s). |
There was a problem hiding this comment.
Pull request overview
This PR targets Windows WebView2 theme integration by mapping MAUI/WinUI effective theme to WebView2’s CoreWebView2.Profile.PreferredColorScheme, so prefers-color-scheme in web content aligns with the app’s theme rather than the OS theme.
Changes:
- Add a Windows
WebView2UpdateBackgroundextension to setDefaultBackgroundColorand adjustPreferredColorScheme. - Wire up
IView.Backgroundmapping for WindowsWebViewHandlerand apply it whenCoreWebView2initializes. - Add a HostApp repro page and an Appium UI test for issue #34823.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Core/src/Platform/Windows/WebViewExtensions.cs | Implements background + preferred color scheme update logic for WebView2. |
| src/Core/src/Handlers/WebView/WebViewHandler.cs | Adds Windows mapper entry to update WebView background. |
| src/Core/src/Handlers/WebView/WebViewHandler.Windows.cs | Adds MapBackground and invokes background/theme update on WebView2 initialization. |
| src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34823.cs | Adds UI tests validating WebView appearance under light/dark app themes. |
| src/Controls/tests/TestCases.HostApp/Issues/Issue34823.cs | Adds HostApp reproduction UI with theme toggle + navigation to a WebView page. |
Comments suppressed due to low confidence (1)
src/Core/src/Handlers/WebView/WebViewHandler.Windows.cs:399
- PreferredColorScheme is only updated when UpdateBackground is invoked (on CoreWebView2Initialized and when Background changes). If the app theme changes while the WebView is already displayed, nothing re-applies PreferredColorScheme, so the WebView can stay stuck on the previous scheme. Consider subscribing to platformView.ActualThemeChanged (and unsubscribing on disconnect) to call UpdateBackground (or just update PreferredColorScheme) whenever the effective theme changes.
if (Handler is WebViewHandler handler)
{
sender.UpdateUserAgent(handler.VirtualView);
sender.UpdateBackground(handler.VirtualView);
if (sender.Source is not null)
{
handler.SyncPlatformCookies(sender.Source.ToString()).FireAndForget();
}
| [nameof(IView.FlowDirection)] = MapFlowDirection, | ||
| [nameof(IView.Background)] = MapBackground, |
There was a problem hiding this comment.
The WINDOWS section of the property mapper is mis-indented compared to the surrounding entries, which makes it easy to miss mappings in reviews/merges. Align the indentation with the other mapper entries inside the initializer.
| [nameof(IView.FlowDirection)] = MapFlowDirection, | |
| [nameof(IView.Background)] = MapBackground, | |
| [nameof(IView.FlowDirection)] = MapFlowDirection, | |
| [nameof(IView.Background)] = MapBackground, |
| //TODO: Make it public in .NET 11. | ||
| internal static void MapBackground(IWebViewHandler handler, IWebView webView) | ||
| { | ||
| handler.PlatformView?.UpdateBackground(webView); | ||
| } |
There was a problem hiding this comment.
This TODO references ".NET 11" and suggests making MapBackground public, but mapper methods are internal implementation details and the repo is targeting main. Please either remove the TODO or clarify the intended action (e.g., linking to an issue/PR and/or explaining why this needs to be public).
| [Category(UITestCategories.WebView)] | ||
| public void WebViewWithDarkTheme() | ||
| { | ||
| App.WaitForElement("WebButton"); |
There was a problem hiding this comment.
The test taps "ThemeButton" without first waiting for it to exist. For UI test stability, add a WaitForElement("ThemeButton") before tapping it (especially on slower devices/CI).
| App.WaitForElement("WebButton"); | |
| App.WaitForElement("WebButton"); | |
| App.WaitForElement("ThemeButton"); |
| helpPage.NavigatedTo += async (_, __) => | ||
| { | ||
| webView.Source = new HtmlWebViewSource | ||
| { | ||
| Html = """ | ||
| <html> | ||
| <head> | ||
| <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"> | ||
| <title>Preparing Help</title> | ||
| <style> | ||
| @media (prefers-color-scheme: dark) { | ||
| html, body { | ||
| color: white; | ||
| background-color: black; | ||
| } | ||
| } | ||
|
|
||
| @media (prefers-color-scheme: light) { | ||
| html, body { | ||
| color: black; | ||
| background-color: white; | ||
| } | ||
| } | ||
| </style> | ||
| </head> | ||
| <body> | ||
| <center><h1>Text on a web page</h1></center> | ||
| </body> | ||
| </html> | ||
| """ | ||
| }; | ||
| }; |
There was a problem hiding this comment.
WebView content is assigned via the NavigatedTo event handler, which introduces timing variability (and the handler is marked async but doesn't await). Consider setting webView.Source before PushAsync (or using a non-async handler) to reduce race/flakiness and remove the unnecessary async event subscription.
| platformWebView.CoreWebView2.Profile.PreferredColorScheme = CoreWebView2PreferredColorScheme.Light; | ||
| } | ||
| } | ||
| else | ||
| { | ||
| if (platformWebView.CoreWebView2 is not null) | ||
| { | ||
| platformWebView.CoreWebView2.Profile.PreferredColorScheme = platformWebView.ActualTheme switch | ||
| { | ||
| ElementTheme.Dark => CoreWebView2PreferredColorScheme.Dark, | ||
| ElementTheme.Light => CoreWebView2PreferredColorScheme.Light, | ||
| _ => CoreWebView2PreferredColorScheme.Auto | ||
| }; | ||
| } | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
When a non-null Background is set, PreferredColorScheme is forced to Light. That can conflict with the app/theme (e.g., if an app sets a dark WebView background for dark mode, web content using prefers-color-scheme will still get Light). Consider deriving PreferredColorScheme from platformWebView.ActualTheme in this branch as well, or basing it on the background color’s luminance if the intent is readability.
| platformWebView.CoreWebView2.Profile.PreferredColorScheme = CoreWebView2PreferredColorScheme.Light; | |
| } | |
| } | |
| else | |
| { | |
| if (platformWebView.CoreWebView2 is not null) | |
| { | |
| platformWebView.CoreWebView2.Profile.PreferredColorScheme = platformWebView.ActualTheme switch | |
| { | |
| ElementTheme.Dark => CoreWebView2PreferredColorScheme.Dark, | |
| ElementTheme.Light => CoreWebView2PreferredColorScheme.Light, | |
| _ => CoreWebView2PreferredColorScheme.Auto | |
| }; | |
| } | |
| } | |
| } | |
| platformWebView.CoreWebView2.Profile.PreferredColorScheme = GetPreferredColorScheme(platformWebView.ActualTheme); | |
| } | |
| } | |
| else | |
| { | |
| if (platformWebView.CoreWebView2 is not null) | |
| { | |
| platformWebView.CoreWebView2.Profile.PreferredColorScheme = GetPreferredColorScheme(platformWebView.ActualTheme); | |
| } | |
| } | |
| } | |
| static CoreWebView2PreferredColorScheme GetPreferredColorScheme(ElementTheme actualTheme) => | |
| actualTheme switch | |
| { | |
| ElementTheme.Dark => CoreWebView2PreferredColorScheme.Dark, | |
| ElementTheme.Light => CoreWebView2PreferredColorScheme.Light, | |
| _ => CoreWebView2PreferredColorScheme.Auto | |
| }; |
kubaflo
left a comment
There was a problem hiding this comment.
Could you please add a snapshot?
@kubaflo , I have added the snapshots for all platform |
kubaflo
left a comment
There was a problem hiding this comment.
Could you please review the ai's summary?
…ability (#35133) <!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! > **Depends on #35136** (pipeline category detection — should merge first) ## What this does Two things: ### 1. UI test category detection in PR review During the PR review workflow, Step 0.5 detects which UI test categories the PR impacts and writes the result to the AI summary comment. This gives reviewers visibility into which UI tests are relevant. **Detection** reuses the 3-tier script from #35136 (test attributes → source paths → AI reasoning). **AI summary** shows a new 🧪 UI Tests section with detected categories before the gate section. ### 2. Gate reliability fixes Multiple fixes to make the gate (`verify-tests-fail.ps1`) more deterministic: | Fix | Problem it solves | |-----|-------------------| | **Absolute path resolution** | Gate scripts not found on Linux CI agents (`Resolve-Path`, `GetFullPath`) | | **File existence check** | Instant cryptic failure when verify script is missing — now logs clear error | | **3x retry on ENV ERROR** | Emulator timeouts, ADB failures, app crashes — transient issues that pass on retry | | **Strip bad report blocks** | Old verify script produces `Passed: False` with empty counts — stripped instead of shown | | **Gate log in fallback** | When report is missing, shows last 20 lines of gate output instead of just `❌ FAILED / Platform: IOS` | ## Files | File | Changes | |------|---------| | `.github/scripts/Review-PR.ps1` | Step 0.5 category detection + all 5 gate fixes | | `.github/scripts/post-ai-summary-comment.ps1` | Add `uitests` phase to render detected categories | | `.github/pr-review/pr-preflight.md` | Step 7: AI identifies impacted UI test categories | ## Validation — PR reviewer builds (Apr 26) 10 builds against real PRs — all succeeded ✅. Category detection shown in AI summary comment. | PR | Categories Detected | Build | AI Summary | |----|-------------------|-------|------------| | #35037 (WebView theme) | `ViewBaseTests,WebView` | [13940071](https://devdiv.visualstudio.com/DevDiv/_build/results?buildId=13940071) | [comment](#35037 (comment)) | | #35031 (Shell memory leak) | `Shell` | [13940072](https://devdiv.visualstudio.com/DevDiv/_build/results?buildId=13940072) | [comment](#35031 (comment)) | | #35020 (XAML Hot Reload) | _(none — XAML only)_ | [13940073](https://devdiv.visualstudio.com/DevDiv/_build/results?buildId=13940073) | ✅ Shows "No UI test categories" | | #35008 (Shell SearchHandler) | `Shell` | [13940074](https://devdiv.visualstudio.com/DevDiv/_build/results?buildId=13940074) | ✅ | | #34997 (RadioButton gradient) | `RadioButton,ViewBaseTests` | [13940075](https://devdiv.visualstudio.com/DevDiv/_build/results?buildId=13940075) | ✅ | | #34980 (DatePicker rotation) | `ViewBaseTests` | [13940076](https://devdiv.visualstudio.com/DevDiv/_build/results?buildId=13940076) | ✅ | | #34974 (Picker CharacterSpacing) | `ViewBaseTests` | [13940077](https://devdiv.visualstudio.com/DevDiv/_build/results?buildId=13940077) | ✅ | | #34923 (SwipeView threshold) | `SwipeView,ViewBaseTests` | [13940078](https://devdiv.visualstudio.com/DevDiv/_build/results?buildId=13940078) | ✅ | | #34907 (CollectionView ScrollTo) | `CollectionView` | [13940079](https://devdiv.visualstudio.com/DevDiv/_build/results?buildId=13940079) | ✅ | | #34845 (RefreshView binding) | `RefreshView,ViewBaseTests` | [13940080](https://devdiv.visualstudio.com/DevDiv/_build/results?buildId=13940080) | ✅ | --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
kubaflo
left a comment
There was a problem hiding this comment.
Could you please resolve conflicts?
|
|
294dc1f to
c88039d
Compare
Note
Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment whether this change resolves your issue. Thank you!
Issue Details
WebView2 on Windows hosts web content in a separate Edge/Chromium browser process that is architecturally decoupled from the WinUI element tree. When MAUI applies an app theme on Windows (e.g., AppTheme.Dark), it sets RequestedTheme on the root FrameworkElement, which propagates to all WinUI controls via normal element tree inheritance.
However, CoreWebView2.Profile.PreferredColorScheme — the browser-level setting that answers web content's prefers-color-scheme CSS media query — is never updated. Its default value is Auto, which reads the OS system theme directly, not the MAUI app theme. So if the user sets AppTheme.Dark in the app while the OS is in light mode, all WinUI controls go dark but the WebView still renders in light mode.
Description of Change
Added PreferredColorScheme logic in UpdateBackground(else part) extension method. It reads WebView2.ActualTheme effective WinUI element theme after inheritance) and maps it to the corresponding CoreWebView2PreferredColorScheme:
Note: This PR includes code changes from PR #34599 My fix is placed in the else block of UpdateBackground, so it needs to be rebased once PR #34599 is merged.
Issues Fixed
Fixes #34823
Tested the behavior in the following platforms.
Before.mp4
After.mp4