[Android] Fix screenshot from WebView content not working#35384
Conversation
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 35384Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 35384" |
There was a problem hiding this comment.
Pull request overview
This PR addresses Android screenshot capture returning blank images when the UI contains hardware-accelerated surfaces (e.g., WebView) by adding a PixelCopy-based capture path (API 26+) and introducing a regression UI test + sample update to validate the scenario.
Changes:
- Update
ScreenshotAndroid implementation to attempt capture viaPixelCopy(API 26+) with fallback to existing canvas/drawing-cache rendering. - Enhance the Essentials Screenshot sample page to include a
WebViewand additional controls. - Add a new Android-focused UI test + HostApp issue page to exercise screenshot capture with
WebViewcontent.
Reviewed changes
Copilot reviewed 4 out of 5 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
| src/Essentials/src/Screenshot/Screenshot.android.cs | Adds PixelCopy async rendering path for Android screenshot capture with fallbacks. |
| src/Essentials/samples/Samples/View/ScreenshotPage.xaml | Updates sample UI to include a WebView to demonstrate/validate screenshot capture. |
| src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue30010.cs | Adds an Android UI test verifying screenshot capture/display when a WebView is present. |
| src/Controls/tests/TestCases.HostApp/Issues/Issue30010.cs | Adds HostApp reproduction page to capture screenshot and display it in an Image. |
|
|
||
| public void OnPixelCopyFinished(int copyResult) | ||
| { | ||
| if (copyResult == (int)PixelCopyResult.Success) |
| return bitmap; | ||
| } | ||
|
|
||
| return RenderUsingCanvasDrawing(view) ?? RenderUsingDrawingCache(view); |
| PixelCopy.Request(window, rect, bitmap, | ||
| new PixelCopyFinishedListener(tcs, bitmap), |
| App.Tap("TakeScreenshotButton"); | ||
|
|
||
| // Wait for the screenshot to be captured and displayed | ||
| var statusLabel = App.WaitForElement("StatusLabel"); |
| var screenshot = await Screenshot.CaptureAsync(); | ||
| var stream = await screenshot.OpenReadAsync(ScreenshotFormat.Png); | ||
|
|
||
| // Read into a byte array so the stream can be consumed multiple times | ||
| using var ms = new MemoryStream(); | ||
| await stream.CopyToAsync(ms); | ||
| var bytes = ms.ToArray(); |
| { | ||
| try | ||
| { | ||
| var screenshot = await Screenshot.CaptureAsync(); |
| // Wait for WebView to finish loading | ||
| App.WaitForElement("StatusLabel"); | ||
| App.WaitForElement("TakeScreenshotButton"); | ||
|
|
||
| // The button is enabled only after WebView.Navigated fires | ||
| App.WaitForElement("TakeScreenshotButton"); | ||
| App.Tap("TakeScreenshotButton"); | ||
|
|
||
| // Wait for the screenshot to be captured and displayed | ||
| var statusLabel = App.WaitForElement("StatusLabel"); | ||
| App.WaitForElement("ResultImage"); |
Use Android's PixelCopy API (API 26+) to capture hardware-accelerated surfaces including WebView's SurfaceView, instead of toggling layer types and sleeping the UI thread. The PixelCopy approach: - Correctly captures hardware-rendered content (WebView, SurfaceView) - Is fully async with no UI thread blocking - Falls back to existing canvas/drawing-cache methods on older APIs Also fixes the test page's stream lifecycle (uses MemoryStream copy instead of a one-shot stream) and removes Thread.Sleep from the test. Fixes #30010 Co-authored-by: Javier Suárez <6755973+jsuarezruiz@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
36434e3 to
febd7d4
Compare
|
/azp run maui-pr-uitests |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/azp run maui-pr, maui-pr-uitests |
|
Azure Pipelines successfully started running 2 pipeline(s). |
The previous commit broke the build with three issues: - Removed the using Microsoft.Maui.Platform import — Microsoft.Maui.Essentials does not reference Microsoft.Maui.Core, so that namespace is not visible. Inlined the small GetActivity helper that was the only thing pulled in from there. - Added using Android.OS so Handler/Looper resolve correctly (the bare Android prefix gets shadowed by Microsoft.Android in this assembly). - Added #nullable enable to match the convention used in the other Essentials Android sources (Battery.android.cs, Browser.android.cs, etc.) so the new Bitmap?, Activity?, and View? annotations compile. Tightened up a couple of pre-existing nullability gaps that the enable surfaced: - WindowManager is now declared as IWindowManager? to match what the as-cast actually returns. - CaptureAsync(Activity) now awaits CaptureAsync(View) and throws InvalidOperationException if the inner result is null, so it actually honours its non-null Task<IScreenshotResult> contract instead of silently propagating null. - CaptureAsync(View) now returns Task<IScreenshotResult?> to match the IPlatformScreenshot interface declaration. - ActivityStateManager.Default.GetCurrentActivity(true) result is now null-checked before being passed to CaptureAsync(Activity). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
/review -b feature/regression-check -p android |
1 similar comment
|
/review -b feature/regression-check -p android |
|
/review -b feature/refactor-copilot-yml |
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 6 findings
See inline comments for details.
| { | ||
| if (OperatingSystem.IsAndroidVersionAtLeast(26)) | ||
| { | ||
| var bitmap = await RenderUsingPixelCopyAsync(view).ConfigureAwait(false); |
There was a problem hiding this comment.
[major] Async and threading safety — If PixelCopy fails, this continuation resumes after ConfigureAwait(false) and then falls back to RenderUsingCanvasDrawing(view) / RenderUsingDrawingCache(view) on a threadpool thread. Those fallback paths touch Android View APIs and must run on the UI thread. Keep this continuation on the main thread or explicitly dispatch fallback rendering back to the UI thread.
| try | ||
| { | ||
| PixelCopy.Request(window, rect, bitmap, | ||
| new PixelCopyFinishedListener(tcs, bitmap), |
There was a problem hiding this comment.
[major] Android callback lifetime — The PixelCopyFinishedListener is allocated inline while the async request is pending. This diverges from the repository's existing PixelCopy pattern, which keeps the listener in managed state until the callback is reached. Store the listener in a local/state object that remains alive until OnPixelCopyFinished completes.
|
|
||
| public void OnPixelCopyFinished(int copyResult) | ||
| { | ||
| if (copyResult == (int)PixelCopyResult.Success) |
There was a problem hiding this comment.
[major] PixelCopy success check — PixelCopyResult.Success is not used elsewhere in this repo and does not match the existing Android PixelCopy pattern, which checks copyResult == 0. Use the established success check for consistency with the target Android binding surface.
| App.Tap("TakeScreenshotButton"); | ||
|
|
||
| // Wait for the screenshot to be captured and displayed | ||
| var statusLabel = App.WaitForElement("StatusLabel"); |
There was a problem hiding this comment.
[major] Test waits for pre-existing UI instead of capture completion — This waits for StatusLabel/ResultImage, but both elements already exist before the screenshot is captured and before the Image source finishes loading. The test can snapshot the pre-capture UI and fail to validate the WebView screenshot fix. Wait for a deterministic post-capture signal such as status text Screenshot captured before calling VerifyScreenshot.
| try | ||
| { | ||
| var screenshot = await Screenshot.CaptureAsync(); | ||
| var stream = await screenshot.OpenReadAsync(ScreenshotFormat.Png); |
There was a problem hiding this comment.
[moderate] Screenshot stream is not disposed — The stream returned from OpenReadAsync is copied and then abandoned without disposal. Wrap it in using before copying to the byte array so repeated screenshot test runs do not leak the native/managed stream resource.
| <Label Text="Some Controls" /> | ||
| <Switch OnColor="Orange" | ||
| ThumbColor="Green" /> | ||
| <WebView> |
There was a problem hiding this comment.
[moderate] Sample WebView needs an explicit height — This sample adds a WebView inside a StackLayout nested in a ScrollView without an explicit height. On MAUI layouts, WebView commonly measures to no visible height in this configuration, so the sample may not actually exercise WebView screenshot capture. Set a HeightRequest like the regression page does.
Use the activity window for PixelCopy captures and keep the PixelCopy listener alive until the async callback completes. Update the regression test to validate the captured screenshot contains a WebView-rendered marker instead of relying on Appium visual baselines. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
/review -b feature/refactor-copilot-yml -p android |
|
/azp run maui-pr-uitests, maui-pr-devicetests |
|
Azure Pipelines successfully started running 2 pipeline(s). |
|
/review -b feature/refactor-copilot-yml -p windows |
MauiBot
left a comment
There was a problem hiding this comment.
🤖 Automated review — alternative fix proposed
The expert-reviewer evaluation compared the PR fix against #2 automatically generated candidates and selected try-fix-2 as the strongest fix.
Why: try-fix-2 won because it passed the Android regression run while the PR gate failed, and its targeted PixelCopy compositing is less invasive than the PR whole-window replacement or try-fix-1 software-layer mutation. It also had clean self-review findings and addresses the PR review concerns around listener disposal and fragile UI-thread fallback behavior.
Please consider applying the candidate diff below (or use it as guidance). Once you push an update, this workflow will re-trigger and re-evaluate.
Candidate diff (`try-fix-2`)
diff --git a/src/Essentials/src/Screenshot/Screenshot.android.cs b/src/Essentials/src/Screenshot/Screenshot.android.cs
index 0b8f1a32fc..f92b60a39d 100644
--- a/src/Essentials/src/Screenshot/Screenshot.android.cs
+++ b/src/Essentials/src/Screenshot/Screenshot.android.cs
@@ -1,11 +1,16 @@
+#nullable enable
+
using System;
+using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Android.App;
using Android.Content;
using Android.Graphics;
+using Android.OS;
using Android.Views;
+using AndroidWebView = Android.Webkit.WebView;
using Java.Nio;
using Microsoft.Maui.ApplicationModel;
@@ -13,7 +18,7 @@ namespace Microsoft.Maui.Media
{
partial class ScreenshotImplementation : IPlatformScreenshot, IScreenshot
{
- static IWindowManager WindowManager =>
+ static IWindowManager? WindowManager =>
Application.Context.GetSystemService(Context.WindowService) as IWindowManager;
public bool IsCaptureSupported => true;
@@ -23,41 +28,208 @@ namespace Microsoft.Maui.Media
if (WindowManager?.DefaultDisplay?.Flags.HasFlag(DisplayFlags.Secure) == true)
throw new UnauthorizedAccessException("Unable to take a screenshot of a secure window.");
- var activity = ActivityStateManager.Default.GetCurrentActivity(true);
+ var activity = ActivityStateManager.Default.GetCurrentActivity(true)
+ ?? throw new InvalidOperationException("Unable to find the current activity.");
return CaptureAsync(activity);
}
- public Task<IScreenshotResult> CaptureAsync(Activity activity)
+ public async Task<IScreenshotResult> CaptureAsync(Activity activity)
{
- var view = activity?.Window?.DecorView?.RootView;
+ var window = activity?.Window;
+ var view = window?.DecorView?.RootView;
if (view == null)
throw new InvalidOperationException("Unable to find the main window.");
- return CaptureAsync(view);
+ var result = await CaptureAsync(view, window).ConfigureAwait(false);
+ return result ?? throw new InvalidOperationException("Unable to capture screenshot.");
}
- public Task<IScreenshotResult> CaptureAsync(View view)
+ public Task<IScreenshotResult?> CaptureAsync(View view) =>
+ CaptureAsync(view, GetActivity(view?.Context)?.Window);
+
+ async Task<IScreenshotResult?> CaptureAsync(View view, Window? window)
{
_ = view ?? throw new ArgumentNullException(nameof(view));
- var bitmap = Render(view);
- var result = bitmap is null ? null : new ScreenshotResult(bitmap);
-
- return Task.FromResult<IScreenshotResult>(result);
+ var bitmap = await RenderAsync(view, window).ConfigureAwait(false);
+ return bitmap is null ? null : new ScreenshotResult(bitmap);
}
- static Bitmap Render(View view)
+ static async Task<Bitmap?> RenderAsync(View view, Window? window)
{
- var bitmap = RenderUsingCanvasDrawing(view);
+ // Base render via the regular canvas path. This captures the entire view
+ // hierarchy correctly except for views backed by their own GPU surface
+ // (WebView, SurfaceView, etc.), which Draw() leaves as transparent holes.
+ var bitmap = RenderUsingCanvasDrawing(view) ?? RenderUsingDrawingCache(view);
+ if (bitmap is null)
+ return null;
- if (bitmap == null)
- bitmap = RenderUsingDrawingCache(view);
+ // Targeted patch: walk for hardware-surface descendants and re-capture
+ // only those rectangles via PixelCopy, then composite them in place.
+ // Requires API 26+ for PixelCopy and a Window for the source surface.
+ if (OperatingSystem.IsAndroidVersionAtLeast(26) && window is not null)
+ {
+ await PatchHardwareSurfacesAsync(view, bitmap, window).ConfigureAwait(false);
+ }
return bitmap;
}
- static Bitmap RenderUsingCanvasDrawing(View view)
+ static void CollectHardwareSurfaceDescendants(View v, List<View> result)
+ {
+ if (v is AndroidWebView)
+ {
+ result.Add(v);
+ return;
+ }
+
+ if (v is SurfaceView)
+ {
+ result.Add(v);
+ return;
+ }
+
+ if (v is ViewGroup group)
+ {
+ var count = group.ChildCount;
+ for (var i = 0; i < count; i++)
+ {
+ var child = group.GetChildAt(i);
+ if (child is not null)
+ CollectHardwareSurfaceDescendants(child, result);
+ }
+ }
+ }
+
+ static async Task PatchHardwareSurfacesAsync(View root, Bitmap target, Window window)
+ {
+ // Snapshot all view-tree state on the caller's (UI) thread BEFORE any await,
+ // so the async loop below never touches View members from a background thread.
+ var surfaces = new List<View>();
+ CollectHardwareSurfaceDescendants(root, surfaces);
+ if (surfaces.Count == 0)
+ return;
+
+ var rootLoc = new int[2];
+ root.GetLocationInWindow(rootLoc);
+
+ var snapshots = new List<(Rect Src, int Width, int Height, int DstX, int DstY)>(surfaces.Count);
+ foreach (var child in surfaces)
+ {
+ if (child.Width <= 0 || child.Height <= 0 || !child.IsAttachedToWindow)
+ continue;
+
+ var childLoc = new int[2];
+ child.GetLocationInWindow(childLoc);
+
+ var src = new Rect(
+ childLoc[0],
+ childLoc[1],
+ childLoc[0] + child.Width,
+ childLoc[1] + child.Height);
+
+ snapshots.Add((src, child.Width, child.Height, childLoc[0] - rootLoc[0], childLoc[1] - rootLoc[1]));
+ }
+
+ if (snapshots.Count == 0)
+ return;
+
+ // From here on, no View access — only Bitmap/Canvas operations.
+ using var canvas = new Canvas(target);
+
+ foreach (var snap in snapshots)
+ {
+ var childBitmap = await CaptureRegionAsync(window, snap.Src, snap.Width, snap.Height).ConfigureAwait(false);
+ if (childBitmap is null)
+ continue;
+
+ try
+ {
+ canvas.DrawBitmap(childBitmap, snap.DstX, snap.DstY, null);
+ }
+ finally
+ {
+ childBitmap.Dispose();
+ }
+ }
+ }
+
+ static Task<Bitmap?> CaptureRegionAsync(Window window, Rect srcRect, int width, int height)
+ {
+ var bitmap = Bitmap.CreateBitmap(width, height, Bitmap.Config.Argb8888!);
+ if (bitmap is null)
+ return Task.FromResult<Bitmap?>(null);
+
+ var tcs = new TaskCompletionSource<Bitmap?>(TaskCreationOptions.RunContinuationsAsynchronously);
+
+ PixelCopyFinishedListener? listener = null;
+ try
+ {
+ listener = new PixelCopyFinishedListener(tcs, bitmap);
+ PixelCopy.Request(window, srcRect, bitmap, listener, new Handler(Looper.MainLooper!));
+ }
+ catch (Exception)
+ {
+ listener?.Dispose();
+ bitmap.Dispose();
+ return Task.FromResult<Bitmap?>(null);
+ }
+
+ return AwaitAndDisposeListenerAsync(tcs.Task, listener);
+ }
+
+ static async Task<Bitmap?> AwaitAndDisposeListenerAsync(Task<Bitmap?> task, PixelCopyFinishedListener listener)
+ {
+ try
+ {
+ return await task.ConfigureAwait(false);
+ }
+ finally
+ {
+ // Keep the JNI listener alive until the callback has fired, then release it.
+ listener.Dispose();
+ }
+ }
+
+ static Activity? GetActivity(Context? context)
+ {
+ while (context is ContextWrapper wrapper)
+ {
+ if (context is Activity activity)
+ return activity;
+ context = wrapper.BaseContext;
+ }
+ return context as Activity;
+ }
+
+ sealed class PixelCopyFinishedListener : Java.Lang.Object, PixelCopy.IOnPixelCopyFinishedListener
+ {
+ readonly TaskCompletionSource<Bitmap?> _tcs;
+ readonly Bitmap _bitmap;
+
+ public PixelCopyFinishedListener(TaskCompletionSource<Bitmap?> tcs, Bitmap bitmap)
+ {
+ _tcs = tcs;
+ _bitmap = bitmap;
+ }
+
+ public void OnPixelCopyFinished(int copyResult)
+ {
+ // PixelCopy.SUCCESS == 0
+ if (copyResult == 0)
+ {
+ _tcs.TrySetResult(_bitmap);
+ }
+ else
+ {
+ _bitmap.Dispose();
+ _tcs.TrySetResult(null);
+ }
+ }
+ }
+
+ static Bitmap? RenderUsingCanvasDrawing(View view)
{
try
{
@@ -81,7 +253,7 @@ namespace Microsoft.Maui.Media
}
}
- static Bitmap RenderUsingDrawingCache(View view)
+ static Bitmap? RenderUsingDrawingCache(View view)
{
#pragma warning disable CS0618 // Type or member is obsolete
#pragma warning disable CA1416 // Validate platform compatibility
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 2 findings
See inline comments for details.
| return bitmap; | ||
| } | ||
|
|
||
| return RenderUsingCanvasDrawing(view) ?? RenderUsingDrawingCache(view); |
There was a problem hiding this comment.
[major] Async and Threading Safety - If CaptureAsync(View) is called from a background thread and PixelCopy returns null, this fallback path runs View.Draw / drawing-cache work on that background thread. Android view rendering APIs must run on the UI thread; dispatch the fallback render to the main looper or fail before falling back.
| else | ||
| { | ||
| _bitmap.Dispose(); | ||
| _tcs.TrySetResult(null); |
There was a problem hiding this comment.
[major] Logic and Correctness - Returning null for any PixelCopy failure silently falls through to the old canvas/drawing-cache screenshot path, which is the path that cannot capture hardware-accelerated WebView content. On an API 26+ device where PixelCopy returns ERROR_SOURCE_NO_DATA/timeout, callers can still receive a successful but blank-prone screenshot instead of a retry or clear failure signal.
|
/review -b feature/refactor-copilot-yml -p android |
MauiBot
left a comment
There was a problem hiding this comment.
🤖 Automated review — alternative fix proposed
The expert-reviewer evaluation compared the PR fix against #1 automatically generated candidates and selected try-fix-1 as the strongest fix.
Why: try-fix-1 wins because it passed the targeted Android regression run while both PR-based candidates are ranked lower due to the failed gate. Its WebView-only PixelCopy overlay approach best targets the Android surface-composition issue while preserving existing canvas screenshot behavior for ordinary content.
Please consider applying the candidate diff below (or use it as guidance). Once you push an update, this workflow will re-trigger and re-evaluate.
Candidate diff (`try-fix-1`)
diff --git a/src/Essentials/src/Screenshot/Screenshot.android.cs b/src/Essentials/src/Screenshot/Screenshot.android.cs
index 0b8f1a32fc..2a448a4447 100644
--- a/src/Essentials/src/Screenshot/Screenshot.android.cs
+++ b/src/Essentials/src/Screenshot/Screenshot.android.cs
@@ -1,19 +1,24 @@
+#nullable enable
+
using System;
+using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Android.App;
using Android.Content;
using Android.Graphics;
+using Android.OS;
using Android.Views;
using Java.Nio;
using Microsoft.Maui.ApplicationModel;
+using AWebView = Android.Webkit.WebView;
namespace Microsoft.Maui.Media
{
partial class ScreenshotImplementation : IPlatformScreenshot, IScreenshot
{
- static IWindowManager WindowManager =>
+ static IWindowManager? WindowManager =>
Application.Context.GetSystemService(Context.WindowService) as IWindowManager;
public bool IsCaptureSupported => true;
@@ -23,41 +28,236 @@ namespace Microsoft.Maui.Media
if (WindowManager?.DefaultDisplay?.Flags.HasFlag(DisplayFlags.Secure) == true)
throw new UnauthorizedAccessException("Unable to take a screenshot of a secure window.");
- var activity = ActivityStateManager.Default.GetCurrentActivity(true);
+ var activity = ActivityStateManager.Default.GetCurrentActivity(true)
+ ?? throw new InvalidOperationException("Unable to find the current activity.");
return CaptureAsync(activity);
}
- public Task<IScreenshotResult> CaptureAsync(Activity activity)
+ public async Task<IScreenshotResult> CaptureAsync(Activity activity)
{
- var view = activity?.Window?.DecorView?.RootView;
+ var window = activity?.Window;
+ var view = window?.DecorView?.RootView;
if (view == null)
throw new InvalidOperationException("Unable to find the main window.");
- return CaptureAsync(view);
+ var result = await CaptureAsync(view, window).ConfigureAwait(false);
+ return result ?? throw new InvalidOperationException("Unable to capture screenshot.");
}
- public Task<IScreenshotResult> CaptureAsync(View view)
+ public Task<IScreenshotResult?> CaptureAsync(View view) =>
+ CaptureAsync(view, GetActivity(view?.Context)?.Window);
+
+ async Task<IScreenshotResult?> CaptureAsync(View view, Window? window)
{
_ = view ?? throw new ArgumentNullException(nameof(view));
- var bitmap = Render(view);
- var result = bitmap is null ? null : new ScreenshotResult(bitmap);
+ var bitmap = await RenderAsync(view, window).ConfigureAwait(false);
+ return bitmap is null ? null : new ScreenshotResult(bitmap);
+ }
+
+ static async Task<Bitmap?> RenderAsync(View view, Window? window)
+ {
+ if (OperatingSystem.IsAndroidVersionAtLeast(26) && window is not null)
+ {
+ var webViewOverlay = await RenderUsingWebViewOverlaysAsync(view, window).ConfigureAwait(false);
+ if (webViewOverlay.HasWebView)
+ return webViewOverlay.Bitmap;
+ }
+
+ return RenderUsingCanvasDrawing(view) ?? RenderUsingDrawingCache(view);
+ }
+
+ static async Task<(bool HasWebView, Bitmap? Bitmap)> RenderUsingWebViewOverlaysAsync(View root, Window window)
+ {
+ var plan = await MainThread.InvokeOnMainThreadAsync(() => CreateWebViewOverlayPlan(root)).ConfigureAwait(false);
+ if (plan is null)
+ return (false, null);
+
+ if (plan.Regions.Count == 0)
+ return (false, null);
+
+ if (plan.Bitmap is null)
+ return (true, null);
+
+ using var canvas = new Canvas(plan.Bitmap);
+ foreach (var region in plan.Regions)
+ {
+ var webViewBitmap = await CaptureWindowRegionAsync(window, region.SourceRect, region.SourceRect.Width(), region.SourceRect.Height()).ConfigureAwait(false);
+ if (webViewBitmap is null)
+ {
+ plan.Bitmap.Dispose();
+ return (true, null);
+ }
+
+ try
+ {
+ canvas.DrawBitmap(webViewBitmap, region.DestinationLeft, region.DestinationTop, null);
+ }
+ finally
+ {
+ webViewBitmap.Dispose();
+ }
+ }
+
+ return (true, plan.Bitmap);
+ }
+
+ static WebViewOverlayPlan? CreateWebViewOverlayPlan(View root)
+ {
+ if (root.Width <= 0 || root.Height <= 0 || !root.IsAttachedToWindow)
+ return null;
+
+ var webViews = new List<View>();
+ CollectVisibleWebViews(root, webViews);
+ if (webViews.Count == 0)
+ return new WebViewOverlayPlan(null, new List<WebViewOverlayRegion>(0));
+
+ var bitmap = RenderUsingCanvasDrawing(root);
+
+ var rootLocation = new int[2];
+ root.GetLocationInWindow(rootLocation);
+
+ var regions = new List<WebViewOverlayRegion>(webViews.Count);
+ for (var i = 0; i < webViews.Count; i++)
+ {
+ var webView = webViews[i];
+ if (webView.Width <= 0 || webView.Height <= 0 || !webView.IsAttachedToWindow)
+ continue;
+
+ var location = new int[2];
+ webView.GetLocationInWindow(location);
+
+ var sourceRect = new Rect(
+ location[0],
+ location[1],
+ location[0] + webView.Width,
+ location[1] + webView.Height);
+
+ regions.Add(new WebViewOverlayRegion(sourceRect, location[0] - rootLocation[0], location[1] - rootLocation[1]));
+ }
+
+ return new WebViewOverlayPlan(bitmap, regions);
+ }
+
+ static void CollectVisibleWebViews(View view, List<View> webViews)
+ {
+ if (view is AWebView && view.Visibility == ViewStates.Visible)
+ {
+ webViews.Add(view);
+ return;
+ }
+
+ if (view is not ViewGroup group)
+ return;
+
+ var childCount = group.ChildCount;
+ for (var i = 0; i < childCount; i++)
+ {
+ var child = group.GetChildAt(i);
+ if (child is not null && child.Visibility == ViewStates.Visible)
+ CollectVisibleWebViews(child, webViews);
+ }
+ }
+
+ static Task<Bitmap?> CaptureWindowRegionAsync(Window window, Rect sourceRect, int width, int height)
+ {
+ var bitmap = Bitmap.CreateBitmap(width, height, Bitmap.Config.Argb8888!);
+ if (bitmap is null)
+ return Task.FromResult<Bitmap?>(null);
+
+ var tcs = new TaskCompletionSource<Bitmap?>(TaskCreationOptions.RunContinuationsAsynchronously);
+ PixelCopyFinishedListener? listener = null;
+ try
+ {
+ listener = new PixelCopyFinishedListener(tcs, bitmap);
+ PixelCopy.Request(window, sourceRect, bitmap, listener, new Handler(Looper.MainLooper!));
+ }
+ catch (Exception)
+ {
+ listener?.Dispose();
+ bitmap.Dispose();
+ return Task.FromResult<Bitmap?>(null);
+ }
+
+ return AwaitPixelCopyAsync(tcs.Task, listener);
+ }
+
+ static async Task<Bitmap?> AwaitPixelCopyAsync(Task<Bitmap?> task, PixelCopyFinishedListener listener)
+ {
+ try
+ {
+ return await task.ConfigureAwait(false);
+ }
+ finally
+ {
+ listener.Dispose();
+ }
+ }
+
+ static Activity? GetActivity(Context? context)
+ {
+ while (context is ContextWrapper wrapper)
+ {
+ if (context is Activity activity)
+ return activity;
+ context = wrapper.BaseContext;
+ }
+ return context as Activity;
+ }
+
+ sealed class WebViewOverlayPlan
+ {
+ public WebViewOverlayPlan(Bitmap? bitmap, List<WebViewOverlayRegion> regions)
+ {
+ Bitmap = bitmap;
+ Regions = regions;
+ }
+
+ public Bitmap? Bitmap { get; }
+ public List<WebViewOverlayRegion> Regions { get; }
+ }
+
+ readonly struct WebViewOverlayRegion
+ {
+ public WebViewOverlayRegion(Rect sourceRect, int destinationLeft, int destinationTop)
+ {
+ SourceRect = sourceRect;
+ DestinationLeft = destinationLeft;
+ DestinationTop = destinationTop;
+ }
- return Task.FromResult<IScreenshotResult>(result);
+ public Rect SourceRect { get; }
+ public int DestinationLeft { get; }
+ public int DestinationTop { get; }
}
- static Bitmap Render(View view)
+ sealed class PixelCopyFinishedListener : Java.Lang.Object, PixelCopy.IOnPixelCopyFinishedListener
{
- var bitmap = RenderUsingCanvasDrawing(view);
+ readonly TaskCompletionSource<Bitmap?> _tcs;
+ readonly Bitmap _bitmap;
- if (bitmap == null)
- bitmap = RenderUsingDrawingCache(view);
+ public PixelCopyFinishedListener(TaskCompletionSource<Bitmap?> tcs, Bitmap bitmap)
+ {
+ _tcs = tcs;
+ _bitmap = bitmap;
+ }
- return bitmap;
+ public void OnPixelCopyFinished(int copyResult)
+ {
+ if (copyResult == 0)
+ {
+ _tcs.TrySetResult(_bitmap);
+ }
+ else
+ {
+ _bitmap.Dispose();
+ _tcs.TrySetResult(null);
+ }
+ }
}
- static Bitmap RenderUsingCanvasDrawing(View view)
+ static Bitmap? RenderUsingCanvasDrawing(View view)
{
try
{
@@ -67,7 +267,7 @@ namespace Microsoft.Maui.Media
var height = view.Height;
var bitmap = Bitmap.CreateBitmap(width, height, Bitmap.Config.Argb8888);
- if (bitmap == null)
+ if (bitmap is null)
return null;
using (var canvas = new Canvas(bitmap))
@@ -81,7 +281,7 @@ namespace Microsoft.Maui.Media
}
}
- static Bitmap RenderUsingDrawingCache(View view)
+ static Bitmap? RenderUsingDrawingCache(View view)
{
#pragma warning disable CS0618 // Type or member is obsolete
#pragma warning disable CA1416 // Validate platform compatibility
|
/review -b feature/refactor-copilot-yml |
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 3 findings
See inline comments for details.
| var listener = new PixelCopyFinishedListener(tcs, bitmap); | ||
| PixelCopy.Request(window, rect, bitmap, | ||
| listener, | ||
| new Handler(Looper.MainLooper!)); |
There was a problem hiding this comment.
[moderate] Android Platform Specifics — This allocates a new Android Handler/Java peer for every screenshot capture. MAUI's existing PixelCopy usage posts through the view's native handler; prefer view.Handler ?? new Handler(Looper.MainLooper!) so the callback is tied to the view looper and avoids unnecessary per-capture allocation.
|
|
||
| try | ||
| { | ||
| return await tcs.Task.ConfigureAwait(true); |
There was a problem hiding this comment.
[major] Async and Threading Safety — This waits indefinitely for the PixelCopy callback. If the window/surface is detached after PixelCopy.Request and no completion arrives, Screenshot.CaptureAsync never completes. Add a bounded wait/cancellation fallback and keep the continuation consistent with the rest of this file (ConfigureAwait(false)) while carefully handling bitmap ownership on timeout vs late success.
| App.Tap("TakeScreenshotButton"); | ||
|
|
||
| // Wait for the screenshot to be captured and validated by the test page | ||
| App.WaitForTextToBePresentInElement("StatusLabel", "Screenshot captured"); |
There was a problem hiding this comment.
[critical] Regression Prevention and Test Coverage — The known gate result showed this test passes without the product fix, so this assertion does not prove the WebView screenshot regression. Strengthen the repro/assertion so it fails on base, e.g. validate actual WebView-specific captured pixels/content rather than only waiting for the host page status label.
🤖 AI Summary
📊 Review Session —
|
| Test | Without Fix (expect FAIL) | With Fix (expect PASS) |
|---|---|---|
🖥️ Issue30010 Issue30010 |
❌ PASS — 857s | ✅ PASS — 1288s |
🔴 Without fix — 🖥️ Issue30010: PASS ❌ · 857s
Determining projects to restore...
Restored /home/vsts/work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 588 ms).
Restored /home/vsts/work/1/s/src/Essentials/src/Essentials.csproj (in 4.23 sec).
Restored /home/vsts/work/1/s/src/Core/src/Core.csproj (in 5.65 sec).
Restored /home/vsts/work/1/s/src/Core/maps/src/Maps.csproj (in 2.22 sec).
Restored /home/vsts/work/1/s/src/Controls/src/Xaml/Controls.Xaml.csproj (in 51 ms).
Restored /home/vsts/work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 26 ms).
Restored /home/vsts/work/1/s/src/Controls/Maps/src/Controls.Maps.csproj (in 34 ms).
Restored /home/vsts/work/1/s/src/Controls/Foldable/src/Controls.Foldable.csproj (in 79 ms).
Restored /home/vsts/work/1/s/src/BlazorWebView/src/Maui/Microsoft.AspNetCore.Components.WebView.Maui.csproj (in 1.15 sec).
Restored /home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj (in 1.7 sec).
1 of 11 projects are up-to-date for restore.
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0-android36.0/Microsoft.Maui.Graphics.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0-android36.0/Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
Core -> /home/vsts/work/1/s/artifacts/bin/Core/Debug/net10.0-android36.0/Microsoft.Maui.dll
Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
Maps -> /home/vsts/work/1/s/artifacts/bin/Maps/Debug/net10.0-android36.0/Microsoft.Maui.Maps.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.Core/Debug/net10.0-android36.0/Microsoft.Maui.Controls.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
Controls.Xaml -> /home/vsts/work/1/s/artifacts/bin/Controls.Xaml/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Xaml.dll
Microsoft.AspNetCore.Components.WebView.Maui -> /home/vsts/work/1/s/artifacts/bin/Microsoft.AspNetCore.Components.WebView.Maui/Debug/net10.0-android36.0/Microsoft.AspNetCore.Components.WebView.Maui.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.Maps/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Maps.dll
Controls.Foldable -> /home/vsts/work/1/s/artifacts/bin/Controls.Foldable/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Foldable.dll
Controls.TestCases.HostApp -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Controls.TestCases.HostApp.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
Graphics -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Graphics.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
Essentials -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
Core -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.dll
Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Maps.dll
Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
Controls.Xaml -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Xaml.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
Microsoft.AspNetCore.Components.WebView.Maui -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.AspNetCore.Components.WebView.Maui.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Maps.dll
Controls.Foldable -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Foldable.dll
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:09:49.31
Broadcasting: Intent { act=android.intent.action.CLOSE_SYSTEM_DIALOGS flg=0x400000 }
Broadcast completed: result=0
Determining projects to restore...
Restored /home/vsts/work/1/s/src/TestUtils/src/VisualTestUtils/VisualTestUtils.csproj (in 1.73 sec).
Restored /home/vsts/work/1/s/src/TestUtils/src/VisualTestUtils.MagickNet/VisualTestUtils.MagickNet.csproj (in 6.12 sec).
Restored /home/vsts/work/1/s/src/Controls/tests/TestCases.Android.Tests/Controls.TestCases.Android.Tests.csproj (in 8.22 sec).
Restored /home/vsts/work/1/s/src/TestUtils/src/UITest.Core/UITest.Core.csproj (in 15 ms).
Restored /home/vsts/work/1/s/src/TestUtils/src/UITest.Appium/UITest.Appium.csproj (in 4 ms).
Restored /home/vsts/work/1/s/src/TestUtils/src/UITest.NUnit/UITest.NUnit.csproj (in 376 ms).
Restored /home/vsts/work/1/s/src/Controls/tests/CustomAttributes/Controls.CustomAttributes.csproj (in 8 ms).
Restored /home/vsts/work/1/s/src/TestUtils/src/UITest.Analyzers/UITest.Analyzers.csproj (in 2.78 sec).
5 of 13 projects are up-to-date for restore.
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
Controls.CustomAttributes -> /home/vsts/work/1/s/artifacts/bin/Controls.CustomAttributes/Debug/net10.0/Controls.CustomAttributes.dll
Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0/Microsoft.Maui.Graphics.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
Core -> /home/vsts/work/1/s/artifacts/bin/Core/Debug/net10.0/Microsoft.Maui.dll
Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.Core/Debug/net10.0/Microsoft.Maui.Controls.dll
UITest.Core -> /home/vsts/work/1/s/artifacts/bin/UITest.Core/Debug/net10.0/UITest.Core.dll
VisualTestUtils -> /home/vsts/work/1/s/artifacts/bin/VisualTestUtils/Debug/netstandard2.0/VisualTestUtils.dll
UITest.NUnit -> /home/vsts/work/1/s/artifacts/bin/UITest.NUnit/Debug/net10.0/UITest.NUnit.dll
UITest.Appium -> /home/vsts/work/1/s/artifacts/bin/UITest.Appium/Debug/net10.0/UITest.Appium.dll
VisualTestUtils.MagickNet -> /home/vsts/work/1/s/artifacts/bin/VisualTestUtils.MagickNet/Debug/netstandard2.0/VisualTestUtils.MagickNet.dll
UITest.Analyzers -> /home/vsts/work/1/s/artifacts/bin/UITest.Analyzers/Debug/netstandard2.0/UITest.Analyzers.dll
Controls.TestCases.Android.Tests -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll
Test run for /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll (.NETCoreApp,Version=v10.0)
VSTest version 18.0.1 (x64)
Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.8.2+699d445a1a (64-bit .NET 10.0.0)
[xUnit.net 00:00:00.21] Discovering: Controls.TestCases.Android.Tests
[xUnit.net 00:00:00.61] Discovered: Controls.TestCases.Android.Tests
NUnit Adapter 4.5.0.0: Test execution started
Running selected tests in /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll
NUnit3TestExecutor discovered 1 of 1 NUnit test cases using Current Discovery mode, Non-Explicit run
>>>>> 05/28/2026 09:34:27 FixtureSetup for Issue30010(Android)
>>>>> 05/28/2026 09:34:29 Issue30010_TakeScreenshotFunctionality Start
>>>>> 05/28/2026 09:34:36 Issue30010_TakeScreenshotFunctionality Stop
Passed Issue30010_TakeScreenshotFunctionality [7 s]
NUnit Adapter 4.5.0.0: Test execution complete
Results File: /home/vsts/work/1/s/CustomAgentLogsTmp/UITests/TestResults/Issue30010.trx
Test Run Successful.
Total tests: 1
Passed: 1
Total time: 1.4311 Minutes
>>> TRX_RESULT_FILE: /home/vsts/work/1/s/CustomAgentLogsTmp/UITests/TestResults/Issue30010.trx
🟢 With fix — 🖥️ Issue30010: PASS ✅ · 1288s
(truncated to last 15,000 chars)
/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010: at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread) [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010: --- End of stack trace from previous location --- [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010: at AndroidDeviceExtensions.PushAndInstallPackageAsync(AndroidDevice device, PushAndInstallCommand command, CancellationToken token) [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010: at AndroidDeviceExtensions.PushAndInstallPackageAsync(AndroidDevice device, PushAndInstallCommand command, CancellationToken token) [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010: at Xamarin.Android.Tasks.FastDeploy.InstallPackage(Boolean installed) [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010: at Xamarin.Android.Tasks.FastDeploy.InstallPackage(Boolean installed) [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010: at Xamarin.Android.Tasks.FastDeploy.RunInstall() [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
Build FAILED.
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010: Mono.AndroidTools.InstallFailedException: Unexpected install output: cmd: Failure calling service package: Broken pipe (32) [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010: [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010: at Mono.AndroidTools.Internal.AdbOutputParsing.CheckInstallSuccess(String output, String packageName) [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010: at Mono.AndroidTools.AndroidDevice.<>c__DisplayClass105_0.<InstallPackage>b__0(Task`1 t) [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010: at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010: --- End of stack trace from previous location --- [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010: at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010: at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread) [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010: --- End of stack trace from previous location --- [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010: at AndroidDeviceExtensions.PushAndInstallPackageAsync(AndroidDevice device, PushAndInstallCommand command, CancellationToken token) [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010: at AndroidDeviceExtensions.PushAndInstallPackageAsync(AndroidDevice device, PushAndInstallCommand command, CancellationToken token) [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010: at Xamarin.Android.Tasks.FastDeploy.InstallPackage(Boolean installed) [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010: at Xamarin.Android.Tasks.FastDeploy.InstallPackage(Boolean installed) [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010: at Xamarin.Android.Tasks.FastDeploy.RunInstall() [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
0 Warning(s)
1 Error(s)
Time Elapsed 00:10:10.92
* daemon not running; starting now at tcp:5037
* daemon started successfully
Determining projects to restore...
All projects are up-to-date for restore.
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0-android36.0/Microsoft.Maui.Graphics.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0-android36.0/Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
Core -> /home/vsts/work/1/s/artifacts/bin/Core/Debug/net10.0-android36.0/Microsoft.Maui.dll
Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
Maps -> /home/vsts/work/1/s/artifacts/bin/Maps/Debug/net10.0-android36.0/Microsoft.Maui.Maps.dll
Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.Core/Debug/net10.0-android36.0/Microsoft.Maui.Controls.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
Microsoft.AspNetCore.Components.WebView.Maui -> /home/vsts/work/1/s/artifacts/bin/Microsoft.AspNetCore.Components.WebView.Maui/Debug/net10.0-android36.0/Microsoft.AspNetCore.Components.WebView.Maui.dll
Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.Maps/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Maps.dll
Controls.Foldable -> /home/vsts/work/1/s/artifacts/bin/Controls.Foldable/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Foldable.dll
Controls.Xaml -> /home/vsts/work/1/s/artifacts/bin/Controls.Xaml/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Xaml.dll
Controls.TestCases.HostApp -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Controls.TestCases.HostApp.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
Graphics -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Graphics.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
Essentials -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
Core -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.dll
Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Maps.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
Controls.Xaml -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Xaml.dll
Microsoft.AspNetCore.Components.WebView.Maui -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.AspNetCore.Components.WebView.Maui.dll
Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Maps.dll
Controls.Foldable -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Foldable.dll
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:08:51.39
Broadcasting: Intent { act=android.intent.action.CLOSE_SYSTEM_DIALOGS flg=0x400000 }
Broadcast completed: result=0
Determining projects to restore...
All projects are up-to-date for restore.
Controls.CustomAttributes -> /home/vsts/work/1/s/artifacts/bin/Controls.CustomAttributes/Debug/net10.0/Controls.CustomAttributes.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0/Microsoft.Maui.Graphics.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
Core -> /home/vsts/work/1/s/artifacts/bin/Core/Debug/net10.0/Microsoft.Maui.dll
Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14218992
Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.Core/Debug/net10.0/Microsoft.Maui.Controls.dll
UITest.Core -> /home/vsts/work/1/s/artifacts/bin/UITest.Core/Debug/net10.0/UITest.Core.dll
UITest.Appium -> /home/vsts/work/1/s/artifacts/bin/UITest.Appium/Debug/net10.0/UITest.Appium.dll
UITest.NUnit -> /home/vsts/work/1/s/artifacts/bin/UITest.NUnit/Debug/net10.0/UITest.NUnit.dll
VisualTestUtils -> /home/vsts/work/1/s/artifacts/bin/VisualTestUtils/Debug/netstandard2.0/VisualTestUtils.dll
VisualTestUtils.MagickNet -> /home/vsts/work/1/s/artifacts/bin/VisualTestUtils.MagickNet/Debug/netstandard2.0/VisualTestUtils.MagickNet.dll
UITest.Analyzers -> /home/vsts/work/1/s/artifacts/bin/UITest.Analyzers/Debug/netstandard2.0/UITest.Analyzers.dll
Controls.TestCases.Android.Tests -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll
Test run for /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll (.NETCoreApp,Version=v10.0)
VSTest version 18.0.1 (x64)
Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.8.2+699d445a1a (64-bit .NET 10.0.0)
[xUnit.net 00:00:00.21] Discovering: Controls.TestCases.Android.Tests
[xUnit.net 00:00:00.57] Discovered: Controls.TestCases.Android.Tests
NUnit Adapter 4.5.0.0: Test execution started
Running selected tests in /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll
NUnit3TestExecutor discovered 1 of 1 NUnit test cases using Current Discovery mode, Non-Explicit run
>>>>> 05/28/2026 09:56:03 FixtureSetup for Issue30010(Android)
>>>>> 05/28/2026 09:56:05 Issue30010_TakeScreenshotFunctionality Start
>>>>> 05/28/2026 09:56:10 Issue30010_TakeScreenshotFunctionality Stop
Passed Issue30010_TakeScreenshotFunctionality [5 s]
NUnit Adapter 4.5.0.0: Test execution complete
Results File: /home/vsts/work/1/s/CustomAgentLogsTmp/UITests/TestResults/Issue30010.trx
Test Run Successful.
Total tests: 1
Passed: 1
Total time: 24.6536 Seconds
>>> TRX_RESULT_FILE: /home/vsts/work/1/s/CustomAgentLogsTmp/UITests/TestResults/Issue30010.trx
⚠️ Failure Details
- ❌ Issue30010 PASSED without fix (should fail) — tests don't catch the bug
📁 Fix files reverted (3 files)
eng/pipelines/ci-copilot.ymlsrc/Essentials/samples/Samples/View/ScreenshotPage.xamlsrc/Essentials/src/Screenshot/Screenshot.android.cs
🧪 UI Tests — Essentials,WebView
Detected UI test categories: Essentials,WebView
❌ Deep UI tests — 0 passed, 49 failed across 2 categories on platform-pool agent (replaces in-process counts above).
🧪 UI Test Execution Results (deep, platform pool)
| Category | Tests | Snapshot diffs |
|---|---|---|
Essentials |
— | — |
WebView |
0/49 (49 ❌) | — |
❌ WebView — 49 failed tests
WebView_SetHtmlSource_VerifyJavaScript
OneTimeSetUp: System.TimeoutException : Timed out waiting for Go To Test button to appear
at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
at Microsoft.Maui.TestCases.Tests.UtilExtensions.NavigateToGallery(IApp app, String page) in /_/src/Controls/tests/TestCases.Shared.Tests/UtilExtensions.cs:line 37
at Microsoft.Maui.TestCases.Tests._GalleryUITest.FixtureSetup() in /_/src/Controls/
...
Issue30010_TakeScreenshotFunctionality
OneTimeSetUp: System.TimeoutException : Loading the captured screenshot from webview content to Image control does not visible
at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
at Microsoft.Maui.TestCases.Tests._IssuesUITest.NavigateToIssue(String issue) in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/_IssuesUITest.cs:line 54
at Microsoft.Maui.TestCases.Tests._IssuesUITes
...
WebView_TestCookieManagement_VerifyAddCookie
OneTimeSetUp: System.TimeoutException : Timed out waiting for Go To Test button to appear
at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
at Microsoft.Maui.TestCases.Tests.UtilExtensions.NavigateToGallery(IApp app, String page) in /_/src/Controls/tests/TestCases.Shared.Tests/UtilExtensions.cs:line 37
at Microsoft.Maui.TestCases.Tests._GalleryUITest.FixtureSetup() in /_/src/Controls/
...
VerifyHybridWebView_EvaluateJavaScriptWithDifferentHybridRoot
OneTimeSetUp: System.TimeoutException : Timed out waiting for Go To Test button to appear
at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
at Microsoft.Maui.TestCases.Tests.UtilExtensions.NavigateToGallery(IApp app, String page) in /_/src/Controls/tests/TestCases.Shared.Tests/UtilExtensions.cs:line 37
at Microsoft.Maui.TestCases.Tests._GalleryUITest.FixtureSetup() in /_/src/Controls/
...
VerifyHybridWebView_SendMessageToJavaScript
OneTimeSetUp: System.TimeoutException : Timed out waiting for Go To Test button to appear
at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
at Microsoft.Maui.TestCases.Tests.UtilExtensions.NavigateToGallery(IApp app, String page) in /_/src/Controls/tests/TestCases.Shared.Tests/UtilExtensions.cs:line 37
at Microsoft.Maui.TestCases.Tests._GalleryUITest.FixtureSetup() in /_/src/Controls/
...
WebView_TestEvaluateJavaScriptAsync_VerifyJavaScriptExecution
OneTimeSetUp: System.TimeoutException : Timed out waiting for Go To Test button to appear
at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
at Microsoft.Maui.TestCases.Tests.UtilExtensions.NavigateToGallery(IApp app, String page) in /_/src/Controls/tests/TestCases.Shared.Tests/UtilExtensions.cs:line 37
at Microsoft.Maui.TestCases.Tests._GalleryUITest.FixtureSetup() in /_/src/Controls/
...
WebView_TestCookieManagement_VerifyAddCookieWithUrlSource
OneTimeSetUp: System.TimeoutException : Timed out waiting for Go To Test button to appear
at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
at Microsoft.Maui.TestCases.Tests.UtilExtensions.NavigateToGallery(IApp app, String page) in /_/src/Controls/tests/TestCases.Shared.Tests/UtilExtensions.cs:line 37
at Microsoft.Maui.TestCases.Tests._GalleryUITest.FixtureSetup() in /_/src/Controls/
...
VerifyWebViewNavigatedEvent
OneTimeSetUp: System.TimeoutException : WebView Navigated event is not triggered
at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
at Microsoft.Maui.TestCases.Tests._IssuesUITest.NavigateToIssue(String issue) in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/_IssuesUITest.cs:line 54
at Microsoft.Maui.TestCases.Tests._IssuesUITest.TryToResetTestState() in /_/src/Controls/tes
...
WebViewDisposesProperly
OneTimeSetUp: System.TimeoutException : [iOS] Cannot access a disposed object. Object name: 'WkWebViewRenderer
at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
at Microsoft.Maui.TestCases.Tests._IssuesUITest.NavigateToIssue(String issue) in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/_IssuesUITest.cs:line 54
at Microsoft.Maui.TestCases.Tests._IssuesUITest.TryToResetTest
...
WebViewLoadedWithoutException
OneTimeSetUp: System.TimeoutException : NullReferenceException throws on Windows when setting Cookies on .NET MAUI WebView
at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
at Microsoft.Maui.TestCases.Tests._IssuesUITest.NavigateToIssue(String issue) in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/_IssuesUITest.cs:line 54
at Microsoft.Maui.TestCases.Tests._IssuesUITest.Tr
...
WebView_SetUrlSource_VerifyNavigatedEvent
OneTimeSetUp: System.TimeoutException : Timed out waiting for Go To Test button to appear
at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
at Microsoft.Maui.TestCases.Tests.UtilExtensions.NavigateToGallery(IApp app, String page) in /_/src/Controls/tests/TestCases.Shared.Tests/UtilExtensions.cs:line 37
at Microsoft.Maui.TestCases.Tests._GalleryUITest.FixtureSetup() in /_/src/Controls/
...
WebView_ValidateDefaultValues_VerifyInitialState
OneTimeSetUp: System.TimeoutException : Timed out waiting for Go To Test button to appear
at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
at Microsoft.Maui.TestCases.Tests.UtilExtensions.NavigateToGallery(IApp app, String page) in /_/src/Controls/tests/TestCases.Shared.Tests/UtilExtensions.cs:line 37
at Microsoft.Maui.TestCases.Tests._GalleryUITest.FixtureSetup() in /_/src/Controls/
...
WebView_VerifyCanGoBackForward
OneTimeSetUp: System.TimeoutException : Timed out waiting for Go To Test button to appear
at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
at Microsoft.Maui.TestCases.Tests.UtilExtensions.NavigateToGallery(IApp app, String page) in /_/src/Controls/tests/TestCases.Shared.Tests/UtilExtensions.cs:line 37
at Microsoft.Maui.TestCases.Tests._GalleryUITest.FixtureSetup() in /_/src/Controls/
...
VerifyWebViewNavigatedEventTriggered
OneTimeSetUp: System.TimeoutException : [Windows] WebView Navigated event called after cancelling it
at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
at Microsoft.Maui.TestCases.Tests._IssuesUITest.NavigateToIssue(String issue) in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/_IssuesUITest.cs:line 54
at Microsoft.Maui.TestCases.Tests._IssuesUITest.TryToResetTestState() in
...
VerifyHybridWebView_SameHybridRootWithDifferentDefaultFile
OneTimeSetUp: System.TimeoutException : Timed out waiting for Go To Test button to appear
at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
at Microsoft.Maui.TestCases.Tests.UtilExtensions.NavigateToGallery(IApp app, String page) in /_/src/Controls/tests/TestCases.Shared.Tests/UtilExtensions.cs:line 37
at Microsoft.Maui.TestCases.Tests._GalleryUITest.FixtureSetup() in /_/src/Controls/
...
WebViewNoCrashPopup
OneTimeSetUp: System.TimeoutException : Fix crash closing Popup with WebView
at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
at Microsoft.Maui.TestCases.Tests._IssuesUITest.NavigateToIssue(String issue) in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/_IssuesUITest.cs:line 54
at Microsoft.Maui.TestCases.Tests._IssuesUITest.TryToResetTestState() in /_/src/Controls/tests/T
...
VerifyHybridWebView_DefaultValues
OneTimeSetUp: System.TimeoutException : Timed out waiting for Go To Test button to appear
at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
at Microsoft.Maui.TestCases.Tests.UtilExtensions.NavigateToGallery(IApp app, String page) in /_/src/Controls/tests/TestCases.Shared.Tests/UtilExtensions.cs:line 37
at Microsoft.Maui.TestCases.Tests._GalleryUITest.FixtureSetup() in /_/src/Controls/
...
VerifyWebViewBackgroundColor
OneTimeSetUp: System.TimeoutException : [iOS] WebView BackgroundColor is not setting correctly
at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
at Microsoft.Maui.TestCases.Tests._IssuesUITest.NavigateToIssue(String issue) in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/_IssuesUITest.cs:line 54
at Microsoft.Maui.TestCases.Tests._IssuesUITest.TryToResetTestState() in /_/sr
...
WebViewShouldNotMirrored
OneTimeSetUp: System.TimeoutException : FlowDirection RightToLeft causes mirrored content in WebView
at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
at Microsoft.Maui.TestCases.Tests._IssuesUITest.NavigateToIssue(String issue) in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/_IssuesUITest.cs:line 54
at Microsoft.Maui.TestCases.Tests._IssuesUITest.TryToResetTestState() in
...
WebViewEvaluateJavaScriptReturnsCorrectResults
OneTimeSetUp: System.TimeoutException : Evaluating javascript in MAUI WebView on IOS returns NULL
at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
at Microsoft.Maui.TestCases.Tests._IssuesUITest.NavigateToIssue(String issue) in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/_IssuesUITest.cs:line 54
at Microsoft.Maui.TestCases.Tests._IssuesUITest.TryToResetTestState() in /_
...
VerifyWebViewDynamicBackgroundColor
OneTimeSetUp: System.TimeoutException : [iOS] WebView BackgroundColor is not setting correctly
at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
at Microsoft.Maui.TestCases.Tests._IssuesUITest.NavigateToIssue(String issue) in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/_IssuesUITest.cs:line 54
at Microsoft.Maui.TestCases.Tests._IssuesUITest.TryToResetTestState() in /_/sr
...
WebView_TestCookieManagement_VerifyAddCookieAndEvaluateJavaScript
OneTimeSetUp: System.TimeoutException : Timed out waiting for Go To Test button to appear
at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
at Microsoft.Maui.TestCases.Tests.UtilExtensions.NavigateToGallery(IApp app, String page) in /_/src/Controls/tests/TestCases.Shared.Tests/UtilExtensions.cs:line 37
at Microsoft.Maui.TestCases.Tests._GalleryUITest.FixtureSetup() in /_/src/Controls/
...
WebView_SetHtmlSource_VerifyNavigatedEvent
OneTimeSetUp: System.TimeoutException : Timed out waiting for Go To Test button to appear
at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
at Microsoft.Maui.TestCases.Tests.UtilExtensions.NavigateToGallery(IApp app, String page) in /_/src/Controls/tests/TestCases.Shared.Tests/UtilExtensions.cs:line 37
at Microsoft.Maui.TestCases.Tests._GalleryUITest.FixtureSetup() in /_/src/Controls/
...
VerifyHybridWebViewWithFlowDirection
OneTimeSetUp: System.TimeoutException : Timed out waiting for Go To Test button to appear
at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
at Microsoft.Maui.TestCases.Tests.UtilExtensions.NavigateToGallery(IApp app, String page) in /_/src/Controls/tests/TestCases.Shared.Tests/UtilExtensions.cs:line 37
at Microsoft.Maui.TestCases.Tests._GalleryUITest.FixtureSetup() in /_/src/Controls/
...
VerifyWebViewWithShadow
OneTimeSetUp: System.TimeoutException : Timed out waiting for Go To Test button to appear
at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
at Microsoft.Maui.TestCases.Tests.UtilExtensions.NavigateToGallery(IApp app, String page) in /_/src/Controls/tests/TestCases.Shared.Tests/UtilExtensions.cs:line 37
at Microsoft.Maui.TestCases.Tests._GalleryUITest.FixtureSetup() in /_/src/Controls/
...
Bugzilla35733Test
OneTimeSetUp: System.TimeoutException : iOS WebView crashes when loading an URL with encoded parameters
at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
at Microsoft.Maui.TestCases.Tests._IssuesUITest.NavigateToIssue(String issue) in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/_IssuesUITest.cs:line 54
at Microsoft.Maui.TestCases.Tests._IssuesUITest.TryToResetTestState()
...
WebViewEvalCrashesOnAndroidWithLongString
OneTimeSetUp: System.TimeoutException : [Android] WebView.Eval crashes on Android with long string
at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
at Microsoft.Maui.TestCases.Tests._IssuesUITest.NavigateToIssue(String issue) in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/_IssuesUITest.cs:line 54
at Microsoft.Maui.TestCases.Tests._IssuesUITest.TryToResetTestState() in /
...
WebView_TestEvaluateJavaScriptAsync_VerifyJavaScriptExecutionWithMultiplePages
OneTimeSetUp: System.TimeoutException : Timed out waiting for Go To Test button to appear
at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
at Microsoft.Maui.TestCases.Tests.UtilExtensions.NavigateToGallery(IApp app, String page) in /_/src/Controls/tests/TestCases.Shared.Tests/UtilExtensions.cs:line 37
at Microsoft.Maui.TestCases.Tests._GalleryUITest.FixtureSetup() in /_/src/Controls/
...
WebView_TestClearCookies_VerifyCookiesCleared
OneTimeSetUp: System.TimeoutException : Timed out waiting for Go To Test button to appear
at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
at Microsoft.Maui.TestCases.Tests.UtilExtensions.NavigateToGallery(IApp app, String page) in /_/src/Controls/tests/TestCases.Shared.Tests/UtilExtensions.cs:line 37
at Microsoft.Maui.TestCases.Tests._GalleryUITest.FixtureSetup() in /_/src/Controls/
...
VerifyHybridWebView_EvaluateJavaScriptWithDifferentDefaultFile
OneTimeSetUp: System.TimeoutException : Timed out waiting for Go To Test button to appear
at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
at Microsoft.Maui.TestCases.Tests.UtilExtensions.NavigateToGallery(IApp app, String page) in /_/src/Controls/tests/TestCases.Shared.Tests/UtilExtensions.cs:line 37
at Microsoft.Maui.TestCases.Tests._GalleryUITest.FixtureSetup() in /_/src/Controls/
...
(+19 more — see TRX in artifact)
📎 Download drop-deep-uitests artifact (TRX + snapshot diffs)
🔍 Regression Cross-Reference
🔍 Regression Cross-Reference
🟢 No regression risks detected. No labeled bug-fix PRs in the last 6 months touched the modified files.
🔍 Pre-Flight — Context & Validation
Issue: #30010 - Loading the captured screenshot from webview content to Image control does not visible
PR: #35384 - Android Screenshot/WebView capture fix
Platforms Affected: Android
Files Changed: 4 implementation/build/sample, 2 test, plus Copilot pipeline support files
Key Findings
- GitHub CLI authentication was unavailable, so pre-flight used the checked-out
pr-review-35384branch, local diffs, gate artifacts, and existing PR state files. - Effective product fix changes
src/Essentials/src/Screenshot/Screenshot.android.csto add an Android PixelCopy capture path for compositor-backed WebView pixels. - Gate result was already failed:
Issue30010passed both without and with fix, so the current repro test does not prove the bug is caught. - Changed test files are UI tests:
src/Controls/tests/TestCases.HostApp/Issues/Issue30010.csandsrc/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue30010.cs; detected category isWebView.
Code Review Summary
Verdict: NEEDS_CHANGES
Confidence: medium
Errors: 3 | Warnings: 5 | Suggestions: 7
Key code review findings:
- ❌ PixelCopy await has no timeout/cancellation path; a missing callback can hang screenshot capture indefinitely.
- ❌
ConfigureAwait(true)on PixelCopy result is inconsistent with surrounding async code and can increase main-thread deadlock risk. - ❌
CaptureAsync(View)can silently fall back to Canvas when no Activity/Window is found, leaving WebView capture broken for that path. ⚠️ Current PR allocates a newHandler(Looper.MainLooper!)per capture; usingview.Handlermatches existing MAUI PixelCopy sample code and avoids extra Java peer allocation.⚠️ UI test is weak because the gate showed it passes without the product fix.
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| PR | PR #35384 | Add Android PixelCopy path before Canvas/DrawingCache fallback | ❌ Gate failed (test passes without fix) | Screenshot.android.cs, Issue30010 test files |
Original PR; root cause direction is sound but validation is weak |
🔬 Code Review — Deep Analysis
I now have all the information needed. Here is the complete, structured review.
Code Review: PR #35384 — Android Screenshot WebView Fix
Context Established from Local Diff
The actual local diff (pr-review-35384 branch, single squashed commit) modifies:
| File | Nature |
|---|---|
src/Essentials/src/Screenshot/Screenshot.android.cs |
Core fix: introduces PixelCopy API path |
src/Controls/tests/TestCases.HostApp/Issues/Issue30010.cs |
New test host-app page |
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue30010.cs |
New UI test |
src/Essentials/samples/Samples/View/ScreenshotPage.xaml |
Sample page update |
Root cause (Issue 30010): Canvas.draw(view) cannot capture hardware-accelerated layers. Android renders WebView content through a dedicated GPU compositor surface that is entirely invisible to a Canvas-based software capture. The fix adds a PixelCopy.Request() code path (API 26+) which reads directly from the GPU frame buffer.
Secondary context — HybridWebView.js: The current HybridWebView.js in src/Core/src/Handlers/HybridWebView/ is the __awaiter/__generator downlevel (ES5-target) TypeScript compilation output. The Core.csproj compiles HybridWebView.ts via Microsoft.TypeScript.MSBuild 5.7.1 and embeds the result as a logical resource _framework/hybridwebview.js. The git history shows repeated cycles of "add downlevel polyfill → revert to ES6" — that churn is the "downlevel-generated JavaScript fix" described in the PR context.
Part 1 — Code Review Findings
🔴 CRITICAL
[C-1] Screenshot.android.cs:~105 — tcs.Task has no timeout or cancellation path
RenderUsingPixelCopyAsync awaits tcs.Task without any timeout or CancellationToken. If PixelCopy.Request() succeeds (no exception) but OnPixelCopyFinished is never invoked — e.g. the Window surface is destroyed between Request() and the callback, or the underlying SurfaceView is detached mid-capture — the Task hangs forever. There is no CancellationToken on any CaptureAsync overload to propagate cancellation.
// PROBLEM: no deadline here
return await tcs.Task.ConfigureAwait(true); // line ~105 in RenderUsingPixelCopyAsyncRequired fix: wrap with Task.WhenAny(tcs.Task, Task.Delay(TimeSpan.FromSeconds(5), ct)), or accept an optional CancellationToken and wire it to tcs.TrySetCanceled().
[C-2] Screenshot.android.cs:~105 — ConfigureAwait(true) is the only non-false await in the file
All other await calls use ConfigureAwait(false). The one await tcs.Task.ConfigureAwait(true) forces resumption on the captured SynchronizationContext (the main thread). OnPixelCopyFinished is already posted to Looper.MainLooper (main thread) and calls TrySetResult. Because TaskCreationOptions.RunContinuationsAsynchronously is set, this schedules the continuation as a queued work item on the main-thread message loop. That is fine in an interactive app. But any caller that blocks on .Result or .GetAwaiter().GetResult() while already on the main thread will deadlock. The inconsistency is confusing and should be ConfigureAwait(false) for consistency unless there is a documented reason to return to the UI thread.
[C-3] Screenshot.android.cs:~63-74 — RenderUsingPixelCopyAsync falls back silently when window is null, leaving WebView screenshots uncaptured
CaptureAsync(View view) resolves a Window via GetActivity(view.Context)?.Window. If the View's Context is not an Activity (e.g. an ApplicationContext, service-level context, or any ContextWrapper that doesn't wrap an Activity), GetActivity returns null, window is null, and RenderUsingPixelCopyAsync returns null early. The code then falls through to RenderUsingCanvasDrawing(view) — which also cannot capture WebView content. The net result is that the entire fix is inert in this code path. Users calling Screenshot.CaptureAsync(view) on a WebView embedded with a non-Activity context get the same broken behavior as before.
🟠 MAJOR
[M-1] Screenshot.android.cs:~88 — new Handler(Looper.MainLooper!) allocates a new Handler per capture; existing DeviceTests uses view.Handler
The existing PixelCopy reference implementation in src/Core/tests/DeviceTests.Shared/ImageAnalysis/RawBitmap.cs:152 passes view.Handler to PixelCopy.Request(...). This is the view's own native handler and requires no allocation. The PR creates new Handler(Looper.MainLooper!) on every call. On Android, Handler creation has JNI overhead and allocates a Java peer. view.Handler should be used when available and non-null; fall back to new Handler(Looper.MainLooper!) only when it is null. Additionally, Looper.MainLooper! null-suppression will throw a NullPointerException in test harness environments where no main looper is prepared.
[M-2] Screenshot.android.cs:~108-116 — Magic constant 0 instead of PixelCopy.Success
if (copyResult == 0) // PixelCopy.SUCCESS == 0The Android binding exposes PixelCopy.Success as a typed constant. Using the magic 0 requires the comment to explain it, and the comment could become stale (if the binding is updated to use an enum). Use (int)PixelCopy.Success or name the literal.
[M-3] Screenshot.android.cs:~43-46 — CaptureAsync(View) public API return type changed from effectively-non-null to explicitly nullable
The old code:
public Task<IScreenshotResult> CaptureAsync(View view) { ... return Task.FromResult<IScreenshotResult>(result); }The new code:
public async Task<IScreenshotResult?> CaptureAsync(View view) { ... }The IPlatformScreenshot interface already declares Task<IScreenshotResult?> CaptureAsync(View view), so the implementation change is conformant to the interface. But external consumers that held a reference typed as ScreenshotImplementation directly and expected a non-null Task<IScreenshotResult> from the concrete class now see a nullable return. The behavioral change (now may return null where before it would have returned null but typed as non-nullable) should be noted in the PR description, and the shared interface contract should be reviewed to ensure callers downstream handle null.
[M-4] Issue30010.cs:~72-84 — Test validation runs inside host app; UI test cannot detect host-app validation failure
The ContainsWebViewMarker logic runs in the host app C# code and only sets _statusLabel.Text. The UI test:
App.WaitForTextToBePresentInElement("StatusLabel", "Screenshot captured");will only pass if ContainsWebViewMarker returns true. But there is no explicit test assertion on "Error:" absence. If the test infrastructure reports "Screenshot captured" due to a timing quirk (race between setting the label and waiting for it), the test passes vacuously. The UI test should additionally assert that _resultImage.Source is non-null, or check that the label text does not contain "Error". More importantly, the ContainsWebViewMarker only samples every 4th pixel in both axes — this 16× subsampling may miss narrow WebView renders on small emulator screen resolutions.
[M-5] Screenshot.android.cs:~112-123 — GetActivity context-unwrap loop: infinite loop risk
while (context is ContextWrapper wrapper)
{
if (context is Activity activity)
return activity;
context = wrapper.BaseContext;
}While extremely rare, a pathological ContextWrapper subclass could return this from BaseContext, creating a cycle. This would spin the loop indefinitely. Add a depth guard (int depth = 0; if (++depth > 10) break;) consistent with AOSP's own context-unwrapping pattern.
🟡 MODERATE
[Mo-1] Screenshot.android.cs:~60 — No documentation of the three-tier fallback strategy
RenderAsync silently tries PixelCopy → Canvas → DrawingCache with no logging at any tier boundary. When PixelCopy returns null (e.g. window is null, view not attached), callers have no signal that they fell through to a tier that cannot capture WebView content. At minimum, a Debug.WriteLine or a structured return type distinguishing "captured but empty" from "fallback used" would aid diagnostics.
[Mo-2] ScreenshotPage.xaml:~18-36 — Indentation inconsistency in sample
The new <StackLayout> block uses 2-space indentation while surrounding <StackLayout> elements use 4-space. MAUI XAML convention is 4-space. This is minor but CI dotnet format does not check XAML indentation.
[Mo-3] HybridWebView.js:18 — __generator references Iterator global — ES2025-era fragility
The downlevel TypeScript compilation emits:
var g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);Iterator (with capital I) is the TC39 Iterator Helpers proposal, shipping in V8 12.2+ (Chrome 122+, Android WebView ≥ March 2024). On older Android WebView versions that don't define Iterator, the ternary falls back to Object.prototype, which is correct. On newer WebViews that do define it, the generator inherits from Iterator.prototype, gaining .map(), .filter(), etc. This is functionally inert for the current code but creates a subtle semantic shift depending on WebView version — the generator object is not the same shape on Android 13 vs. Android 15 devices. This version-dependent prototype chain is a sign that the downlevel compilation target is wrong for the deployment environment.
[Mo-4] HybridWebView.js:~44-271 — Downlevel compilation target mismatch with deployed Android WebView capability
Android WebView has shipped Chromium ≥ 55 since Android 7.0 (API 24, 2016), which natively supports async/await, const/let, arrow functions, and template literals. Devices running API 21–23 receive WebView updates via Google Play independently of the OS version; as of Play Store policy, a WebView supporting ES2017 is installed. The downlevel __awaiter/__generator polyfills add ~40 lines of non-obvious state-machine code for zero gain on any real device. The git history shows 15e99bb "Revert HybridWebView.js to clean ES6 version" and acec196 "Actually revert HybridWebView.js to match origin/main" — the project has already established intent to maintain a clean ES6 output.
[Mo-5] Screenshot.android.cs:~PlatformOpenReadAsync — MemoryStream position not reset before return (pre-existing, exposed by fix)
Task PlatformCopyToAsync(Stream destination, ScreenshotFormat format, int quality)
{
bmp.Compress(f, quality, destination);
return Task.CompletedTask;
}PlatformOpenReadAsync creates a MemoryStream, calls PlatformCopyToAsync, then sets result.Position = 0. This is fine. But PlatformCopyToAsync takes a generic Stream destination and doesn't reset position. If the destination is not seekable, callers piping to a non-seekable stream get no error. Pre-existing, but worth noting since the PR adds a new using var ms = new MemoryStream(); await stream.CopyToAsync(ms) pattern in Issue30010.cs that re-reads after OpenReadAsync — the two-stream approach is correct but unnecessarily round-trips through the Bitmap encoder when a direct PlatformCopyToAsync would suffice.
🔵 MINOR
[Mi-1] Issue30010.cs:~50 — WebView Navigated event subscription never unsubscribed
webView.Navigated += (s, e) => { _statusLabel.Text = "WebView loaded"; ... };The lambda captures _statusLabel and _screenshotButton. The ContentPage holds the webView, which holds the event subscription, which holds a closure back to page fields. This creates a cycle that prevents collection until the page is finalized. In a test page this is acceptable but should use += with a named method or be unsubscribed in OnDisappearing.
[Mi-2] Issue30010.cs:~56 — async void event handler without timeout guard
OnTakeScreenshotClicked is async void — correct for event handlers — and has a try/catch, also correct. But there is no timeout on Screenshot.CaptureAsync(). If the new PixelCopy path hangs (see C-1), the button tap will silently freeze the page with the status label stuck on "WebView loaded".
Part 2 — Root-Cause Hypotheses
Root Cause A — Hardware-Compositor Surface Isolation (Primary)
Android draws all accelerated content — including WebView, SurfaceView, TextureView in surface mode — through a dedicated hardware layer that is composited by SurfaceFlinger. The CPU-side Canvas.draw(view) and DrawingCache approaches only capture what the view renders onto a Canvas. WebView, which manages its own SurfaceView/TextureView child, simply calls drawChild → View.draw(canvas) on that child, which produces nothing (or black) because the actual pixel data lives on the GPU surface, not in the view hierarchy. PixelCopy bypasses this by requesting a copy of a screen rect from the compositor's frame buffer, which includes all layers.
Root Cause B — getDrawingCache() and draw(canvas) API Deprecation
DrawingCache was deprecated in API 28 and was always unreliable for hardware-accelerated views. It worked in API < 11 when all rendering was software. For API 11+, setDrawingCacheEnabled(true) on a hardware-layer view produces a blank bitmap unless the view explicitly supports software rendering fallback. WebView explicitly opts out of software fallback, so DrawingCache returns a black or empty bitmap.
Root Cause C — TypeScript Compilation Target Drift (HybridWebView.js secondary)
The HybridWebView.ts is compiled to HybridWebView.js at build time via Microsoft.TypeScript.MSBuild. The TypeScript configuration (no tsconfig.json committed to src/Core/) means the TypeScript compiler uses its default settings or project-level props. When target is not explicitly set to ES2015+, the TypeScript MSBuild task defaults to ES3 or ES5 and emits __awaiter/__generator. The git churn (15e99bb, acec196, 135e8e8, 9d0aabcb30) shows that contributors repeatedly tried to commit downlevel-compiled JS and then had to revert it, implying the tsconfig.json / TypeScript MSBuild settings are not pinned to an ES6+ target — leaving the generated output sensitive to local TypeScript tooling versions.
Part 3 — Alternative Fix Candidates for the Downlevel JavaScript Issue
Alternative 1: Commit a tsconfig.json pinning "target": "ES2017" in the handler directory
Approach: Add src/Core/src/Handlers/HybridWebView/tsconfig.json:
{
"compilerOptions": {
"target": "ES2017",
"module": "none",
"strict": true,
"noImplicitAny": true,
"outFile": "HybridWebView.js",
"sourceMap": true
},
"files": ["HybridWebView.ts"]
}ES2017 is the minimum that includes native async/await. The module: "none" matches the current IIFE output structure. The generated HybridWebView.js would use native async/await, const/let, arrow functions, and template literals with no polyfill helpers. Android WebView has supported all ES2017 features since Chrome 55 (Android 7.0+ native; API 21–23 via Play Store WebView update). The Core.csproj already has Microsoft.TypeScript.MSBuild 5.7.1 — it would honour the tsconfig.json. This resolves the compilation drift by making the target explicit and reproducible.
Testability: The existing HybridWebView integration tests validate the JS↔.NET messaging contract regardless of the compilation target. No new test infrastructure needed; the compiled JS can be diffed in CI to confirm no polyfills appear.
Tradeoff: Very old devices (API 21–23) without an updated WebView APK could fail. In practice, Google Play has required WebView updates since 2014 and MAUI's minimum API is 21, but adding an explicit note in SUPPORTED_VERSIONS.md is warranted.
Alternative 2: Rewrite invokeDotNet and invokeJavaScript as explicit Promise chains (zero polyfill, zero transpilation concern)
Approach: Remove async/await from HybridWebView.ts and use .then()/.catch() chaining:
function invokeDotNet(methodName: string, paramValues?: any): Promise<any> {
// ... build body / message ...
return fetch(requestUrl, { method: 'POST', headers, body: message })
.then(rawResponse => rawResponse.json())
.then((response: DotNetInvokeResult) => {
if (!response) return null;
if (response.IsError) {
const error = new Error(response.ErrorMessage ?? 'Unknown error');
throw error;
}
return response.IsJson ? JSON.parse(response.Result) : response.Result;
});
}
function invokeJavaScript(taskId: string, methodName: Function, args: any[]): Promise<void> {
return Promise.resolve()
.then(() => methodName(...args))
.then(result => invokeJavaScriptCallbackInDotNet(taskId, result))
.catch(ex => { console.error(ex); invokeJavaScriptFailedInDotNet(taskId, ex); });
}Why this is meaningfully different: This approach requires no TypeScript async/await at all. The TypeScript compiler emits pure ES5-compatible code even without __awaiter/__generator because Promise is used directly. The .ts file becomes compilable at any TypeScript target without producing polyfills. The __generator state machine — which has the Iterator global fragility — disappears entirely.
Testability: Functionally identical to the current implementation; all existing HybridWebView device tests validate messaging correctness. The .then() chain is unit-testable with a mock fetch in a Node.js test runner, which the current __awaiter-based code is not easily isolated from.
Tradeoff: Less readable for TypeScript maintainers accustomed to async/await. The current TypeScript source expresses intent clearly with await — converting to chains sacrifices ergonomics for portability.
Alternative 3: Replace fetch with the existing hybridWebViewHost.sendMessage channel for invokeDotNet on Android (eliminating async/await entirely for Android)
Approach: The current design uses an HTTP fetch to /__hwvInvokeDotNet for .NET method invocation. On Android, the Window.hybridWebViewHost JavaScript interface (a @JavascriptInterface Java object) is already available for JS→.NET communication via sendMessage. The response can be returned through the same message-passing channel (HybridWebViewMessageReceived event) rather than through an HTTP response body.
This would change Android invokeDotNet to:
function invokeDotNet(methodName, paramValues) {
return new Promise(function(resolve, reject) {
var taskId = Math.random().toString(36).slice(2);
// register a one-shot response listener
function onResponse(e) {
var msg = e.detail.message;
if (msg.startsWith('__DotNetResult|' + taskId + '|')) {
window.removeEventListener('HybridWebViewMessageReceived', onResponse);
var payload = JSON.parse(msg.slice(('__DotNetResult|' + taskId + '|').length));
if (payload.IsError) reject(new Error(payload.ErrorMessage));
else resolve(payload.IsJson ? JSON.parse(payload.Result) : payload.Result);
}
}
window.addEventListener('HybridWebViewMessageReceived', onResponse);
window.hybridWebViewHost.sendMessage('__InvokeDotNet|' + taskId + '|' + JSON.stringify({MethodName: methodName, ParamValues: paramValues}));
});
}The C# handler would add a __InvokeDotNet message type and route the response back via EvaluateJavaScript or the existing SendMessage path.
Why this is meaningfully different from both current fix and Alt 1/2: This eliminates the fetch HTTP call entirely on Android, removing the cross-origin fake-URL hack (/__hwvInvokeDotNet) from the Android code path. It also eliminates async/await from the Android code path since the Promise constructor callback is synchronous. Android WebView's JavascriptInterface path (hybridWebViewHost.sendMessage) is guaranteed to work regardless of WebView version or network stack state. The HTTP fetch requires a functioning WebViewClient.shouldInterceptRequest handler — which can fail if the handler is incorrectly registered or if the URL scheme isn't intercepted.
Testability: Requires a new C# handler message type (__InvokeDotNet). Unit-testable: the JavaScript side can be tested with a mock window.hybridWebViewHost in Node.js. Device tests for HybridWebView.InvokeDotNetAsync would exercise the new path.
Tradeoff: A non-trivial architectural change affecting both the JS and the Android C# handler. All three platforms share HybridWebView.ts; the Android-only branch (detected via window.hybridWebViewHost) creates a conditional code path that doesn't apply to iOS/Windows, increasing maintenance surface. This is best suited for a platform-specific .android.ts compilation unit, but the MAUI build infrastructure would need to support per-platform TypeScript compilation.
Alternative 4 (Bonus): Add Babel as a post-compilation polyfill injection step with feature-detection guard
Approach: Add a Babel transform step after tsc compilation that wraps the generated async code in a feature-detection check:
// Injected at top of HybridWebView.js by Babel
if (typeof Promise === 'undefined' || typeof Symbol === 'undefined') {
// load __awaiter / __generator polyfill only here
}Using @babel/preset-env with targets: { chrome: "55" } (matching Android WebView minimum) produces polyfill injection only for the features not available in Chrome 55+. On modern WebViews, the polyfill block is dead code eliminated by the JS engine. On very old WebViews (pre-API 21 with ancient Chromium), the polyfill activates.
Why meaningfully different: This is a build-infrastructure solution rather than a source-code solution. It doesn't require changing the TypeScript source or the .NET handler, only the MSBuild pipeline. The Core.csproj TypeScript compilation target becomes Target AfterTargets="CompileTypeScript" with a Babel invocation.
Testability: The generated output can be snapshot-tested against a reference. The Microsoft.TypeScript.MSBuild NuGet would remain for type-checking; Babel handles the output transform. Adds a node dependency to the MAUI build, which may conflict with the project's dotnet-first build philosophy (see [Build & MSBuild dimension]: "Use dotnet-public feed" / "Build tasks cannot depend on optional NuGet packages").
Summary Table
| ID | Severity | Area | One-line description |
|---|---|---|---|
| C-1 | 🔴 Critical | Android/Async | tcs.Task has no timeout; PixelCopy callback may never fire → infinite hang |
| C-2 | 🔴 Critical | Android/Threading | ConfigureAwait(true) inconsistency + potential main-thread deadlock |
| C-3 | 🔴 Critical | Android/WebView | CaptureAsync(View) with non-Activity context falls back to Canvas → WebView still blank |
| M-1 | 🟠 Major | Android/Performance | new Handler(Looper.MainLooper!) allocation per call; use view.Handler |
| M-2 | 🟠 Major | Android | Magic constant 0 for PixelCopy.SUCCESS |
| M-3 | 🟠 Major | Public API | CaptureAsync(View) return type behavioral change needs documentation |
| M-4 | 🟠 Major | Testability | UI test cannot detect host-app validation failure; ContainsWebViewMarker too coarse |
| M-5 | 🟠 Major | Android | GetActivity context-walk loop has no depth guard |
| Mo-1 | 🟡 Moderate | Diagnostics | No logging at fallback tier boundaries |
| Mo-2 | 🟡 Moderate | Sample | XAML indentation inconsistency |
| Mo-3 | 🟡 Moderate | JS/Android | Iterator global in __generator is ES2025 — version-dependent prototype chain |
| Mo-4 | 🟡 Moderate | JS/Build | Downlevel __awaiter/__generator mismatch with deployed Android WebView capability |
| Mo-5 | 🟡 Moderate | Android | Pre-existing: PlatformCopyToAsync doesn't reset stream position |
| Mi-1 | 🔵 Minor | Memory | webView.Navigated lambda never unsubscribed in test page |
| Mi-2 | 🔵 Minor | Async | OnTakeScreenshotClicked has no timeout guard against PixelCopy hang |
Blocking issues before merge: C-1, C-2, C-3, M-1, M-4 (test must reliably catch regressions), Mo-3 (JS Iterator fragility if the downlevel JS is retained).
🔧 Fix — Analysis & Comparison
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| 1 | try-fix | Temporary software-layer Canvas capture | ✅ PASS | 1 file | Satisfies weak UI test but mutates the view tree and is less robust for compositor-backed WebView content than PixelCopy |
| 2 | try-fix | PixelCopy using view.Handler |
✅ PASS | 1 file | Strongest candidate: same root-cause fix as PR, lower allocation, aligns with existing MAUI PixelCopy sample; still lacks timeout |
| 3 | try-fix | PixelCopy using view.Handler plus 5-second timeout/fallback |
✅ PASS | 1 file | More resilient to missing callbacks, but self-review found bitmap ownership race on late callback |
| PR | PR #35384 | PixelCopy using new main-looper Handler | ❌ Gate failed | 3 product/test files | Original PR; correct root-cause direction, but gate did not prove regression coverage |
Cross-Pollination
| Model | Round | New Ideas? | Details |
|---|---|---|---|
| maui-expert-reviewer | 1 | Yes | Generated distinct approaches: software-layer Canvas, view-handler PixelCopy, bounded PixelCopy, and broader JS/build alternatives that were determined unrelated to the effective branch diff |
| local test loop | 1 | Yes | Candidate 1 passing showed the Issue30010 test is too weak to distinguish root-cause fixes from potentially fragile Canvas workarounds |
| local test loop | 2 | Yes | Candidate 2 passing plus expert warning led to candidate 3 with timeout/fallback |
| local test loop | 3 | No | Candidate 3 exposed a race requiring ownership design; no further meaningfully different low-risk candidate remained without expanding API/cancellation surface |
Exhausted: Yes
Selected Fix: Candidate #2 as the best alternative candidate — it keeps the PixelCopy root-cause fix, reduces per-capture Handler allocation, and follows existing MAUI PixelCopy usage. Candidate #3 may become better after fixing bitmap ownership around timeout/late-callback races. None can be called demonstrably better than the PR from tests alone because the gate test passes without the fix.
📋 Report — Final Recommendation
Comparative Report — PR #35384 Android Screenshot/WebView Fix
Ranking
| Rank | Candidate | Regression status | Assessment |
|---|---|---|---|
| 1 | pr-plus-reviewer |
Not rerun per instruction | Best technical candidate. Preserves the PR's PixelCopy root-cause fix, adopts view.Handler, adds a bounded wait, and guards bitmap ownership so timeout and late PixelCopy callbacks cannot race into double-dispose/returning a disposed bitmap. Also strengthens the test validation by checking the WebView rectangle. |
| 2 | try-fix-2 |
Passed | Strongest completed try-fix. It uses PixelCopy and view.Handler, matching the root cause and existing MAUI PixelCopy patterns. It still has the unbounded callback wait. |
| 3 | try-fix-3 |
Passed | Adds a timeout/fallback and uses view.Handler, but its self-review found a bitmap ownership race between timeout disposal and a late success callback. |
| 4 | try-fix-1 |
Passed | The software-layer Canvas workaround passed the weak test, but it mutates the view tree and is less reliable for compositor-backed WebView pixels than PixelCopy. |
| 5 | pr |
Failed gate | Correct PixelCopy direction, but ranked below candidates that passed regression checks because the gate failed: the test passed without the fix. It also allocates a new Handler per capture and can hang indefinitely if PixelCopy never calls back. |
Decision
Winner: pr-plus-reviewer. It is the only candidate that combines the correct Android PixelCopy root-cause fix with the expert review's actionable improvements and fixes the ownership race identified in the bounded try-fix candidate. The raw PR is not acceptable as submitted because the regression gate failed and the implementation can hang indefinitely; the passing try-fix candidates are useful evidence but either omit timeout handling or implement it with a race.
Notes on Regression Evidence
The known gate result means the current Issue30010 UI test is weak: it passed without the product fix. The try-fix pass results therefore prove the candidates did not obviously break the scenario, but they do not conclusively prove the original regression is covered. This is why the winning candidate includes test-strengthening work in addition to the product fix.
Description of Change
Replaces the approach in #30437 with Android's canonical
PixelCopyAPI (API 26+) for capturing hardware-accelerated surfaces, as recommended by the AI review.Problem: Calling
Screenshot.CaptureAsync()on a page containing aWebViewin Release builds on Android results in a blank/invisible image. The canvas-basedview.Draw()approach cannot capture hardware-rendered surfaces like WebView's internalSurfaceView.Solution: Use
PixelCopy.Request(Window, Rect, Bitmap, ...)which is the Android-recommended API for snapshotting rendered surfaces — including WebView, SurfaceView, TextureView, and other hardware-accelerated views.Key improvements over #30437:
PixelCopyis callback-based and fully async, eliminating theThread.Sleep(50)on the UI threadPixelCopyis the documented Android best practice for hardware surface capture (API 26+)new MemoryStream(bytes)factory pattern instead of capturing a one-shot streamThread.Sleepfrom UITest — waits for UI elements insteadScreenshotis already accessible through MAUI meta-packageBased on the work by @jsuarezruiz in #30437.
Issues Fixed
Fixes #30010
Supersedes #30437