Skip to content

[Android] Fix for RefreshView triggering pull-to-refresh when scrolling inside a WebView with internal scrollable content#34614

Merged
kubaflo merged 13 commits into
dotnet:inflight/currentfrom
BagavathiPerumal:fix-33510
May 14, 2026
Merged

[Android] Fix for RefreshView triggering pull-to-refresh when scrolling inside a WebView with internal scrollable content#34614
kubaflo merged 13 commits into
dotnet:inflight/currentfrom
BagavathiPerumal:fix-33510

Conversation

@BagavathiPerumal
Copy link
Copy Markdown
Contributor

Note

Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!

Issue Details

When using a RefreshView that wraps a WebView in a .NET MAUI app on Android, the pull-to-refresh gesture is triggered as soon as the user scrolls up, even if the WebView content has not reached the top. This prevents normal upward scrolling through web content without accidentally refreshing the page.

Root Cause

The issue occurs because of how Android RefreshView determines whether to intercept a downward drag gesture. For a WebView, this decision is based on the native scroll state (ScrollY / CanScrollVertically(-1)).

In this scenario, the visible content is not scrolled by the native WebView itself, but by an internal HTML container (overflow-y: auto). While this internal DOM element is still mid-scroll, the native WebView may incorrectly report that it is already at the top.

As a result, RefreshView intercepts the gesture too early, triggering pull-to-refresh instead of allowing the web content to continue scrolling.

Description of Change

The fix involves adding Android-specific handling for the WebView + RefreshView interaction.

A lightweight WebView bridge is introduced to determine whether the touched DOM content can still scroll upward. MauiSwipeRefreshLayout uses this information when a gesture starts inside a WebView:

  • If the internal web content is not yet at the top, the gesture remains with the WebView
  • Once the internal web content reaches the top, RefreshView is allowed to intercept and trigger refresh

This approach preserves existing RefreshView behavior for other controls, while correctly handling the WebView scenario that native Android scroll checks cannot accurately detect.

Validated the behavior in the following platforms

  • Android
  • Windows
  • iOS
  • Mac

Issues Fixed

Fixes #33510

Output ScreenShot

Before After
BeforeFix-33510.mov
AfterFix-33510.mov

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 24, 2026

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 34614

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 34614"

@dotnet-policy-service dotnet-policy-service Bot added the partner/syncfusion Issues / PR's with Syncfusion collaboration label Mar 24, 2026
@sheiksyedm sheiksyedm marked this pull request as ready for review March 26, 2026 10:58
Copilot AI review requested due to automatic review settings March 26, 2026 10:58
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses Android-specific RefreshView behavior when wrapping a WebView whose visible scrolling happens inside an internal HTML overflow container (so native WebView scroll state can incorrectly indicate “at top”), causing pull-to-refresh to trigger too early.

Changes:

  • Add an Android WebView JavaScript bridge + injected observer script to report whether touched DOM content can still scroll up.
  • Update MauiSwipeRefreshLayout to consult the reported “can scroll up” state for WebView (and add intercept logic for gestures starting inside a WebView).
  • Add a HostApp repro page and an Android UI test for issue #33510.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/Core/src/PublicAPI/net-android/PublicAPI.Unshipped.txt Records the new/changed Android public surface from overrides added in this PR.
src/Core/src/Platform/Android/RefreshViewWebViewScrollCapture.cs Introduces the JS bridge + observer script and exposes TryGetCanScrollUp for RefreshView decisions.
src/Core/src/Platform/Android/MauiWebViewClient.cs Resets scroll capture state on navigation start and injects the observer script on navigation finish.
src/Core/src/Platform/Android/MauiWebView.cs Attaches/detaches the JS interface lifecycle to the native MauiWebView.
src/Core/src/Platform/Android/MauiSwipeRefreshLayout.cs Uses the bridge-reported scrollability for WebView and adds intercept logic for gestures that start in a WebView.
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33510.cs Adds an Android UI test that validates pull-to-refresh doesn’t trigger until internal web scrolling reaches top.
src/Controls/tests/TestCases.HostApp/Issues/Issue33510.cs Adds a HostApp issue page with a RefreshView + WebView using internal overflow scrolling to reproduce the bug.

Comment thread src/Core/src/Platform/Android/MauiSwipeRefreshLayout.cs
Comment thread src/Core/src/Platform/Android/MauiWebViewClient.cs
Comment thread src/Core/src/Platform/Android/MauiWebView.cs
@MauiBot MauiBot added s/agent-review-incomplete s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) s/agent-changes-requested AI agent recommends changes - found a better alternative or issues s/agent-approved AI agent recommends approval - PR fix is correct and optimal s/agent-fix-pr-picked AI could not beat the PR fix - PR is the best among all candidates and removed s/agent-review-incomplete s/agent-changes-requested AI agent recommends changes - found a better alternative or issues labels Mar 27, 2026
kubaflo
kubaflo previously approved these changes Mar 28, 2026
@sheiksyedm
Copy link
Copy Markdown
Contributor

/azp run maui-pr-uitests , maui-pr-devicetests

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 2 pipeline(s).

@sheiksyedm
Copy link
Copy Markdown
Contributor

/azp run maui-pr-uitests

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 pipeline(s).

@BagavathiPerumal
Copy link
Copy Markdown
Contributor Author

🤖 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 preserves the PR's well-designed JS observer architecture while fixing two functional gaps identified in code review: (1) the __mauiRefreshViewObserverInstalled guard that silently breaks JS re-registration after Shell tab-switch (detach+re-attach without page reload), replaced with named JS event handles that are properly removed/re-added on each injection; (2) MauiHybridWebView inside a RefreshView was unprotected since Attach() was never called for it, now fixed via OnAttachedToWindow/OnDetachedFromWindow/Dispose overrides mirroring MauiWebView.

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)

I've updated the changes based on the Copilot review suggestions to extend the same fixes to MauiHybridWebView.

Specifically:

MauiHybridWebView.cs

  • Added OnAttachedToWindow override calls Attach(this) when the view is inside a SwipeRefreshLayout.
  • Added OnDetachedFromWindow override calls Detach() on removal.
  • Added IsInsideSwipeRefreshLayout helper walks the parent view tree to detect nesting.
  • Added Dispose(bool) override calls Detach() during cleanup to prevent leaks.

MauiHybridWebViewClient.cs

  • Added OnPageStarted override calls Reset() to clear stale scroll state on navigation.
  • Added OnPageFinished override calls InjectObserver() to inject the JS bridge after page load.
  • Added using Android.Graphics; to resolve the CS0234 build error.

PublicAPI.Unshipped.txt

  • Added five new entries for the new public overrides in MauiHybridWebView and MauiHybridWebViewClient.

These changes mirror the existing RefreshView/WebView fix pattern and resolve the same scroll-capture issue for HybridWebView inside SwipeRefreshLayout.

dependabot Bot and others added 5 commits May 7, 2026 08:48
Updated [Magick.NET-Q8-AnyCPU](https://github.com/dlemstra/Magick.NET)
from 14.10.4 to 14.12.0.

<details>
<summary>Release notes</summary>

_Sourced from [Magick.NET-Q8-AnyCPU's
releases](https://github.com/dlemstra/Magick.NET/releases)._

## 14.12.0

### What's Changed
- Added `FixByteOrder` to the `DcmReadDefines` (#​1976)
- Added `IconWriteDefines`.

### Related changes in ImageMagick since the last release of Magick.NET:
- Correct bug in `Composite` when using `CopyAlpha` (#​1985)
- Fixed incorrect orientation of JPEG compressed TIFF images (#​1991)
- Heap-Buffer-Overflow write of single zero byte when parsing xml
(GHSA-cr67-pvmx-2pp2)
- Stack Overflow in DestroyXMLTree
(GHSA-fwvm-ggf6-2p4x)
- Out-of-Bounds read in sample operation
(GHSA-pcvx-ph33-r5vv)
- Stack Overflow via Recursive FX Expression Parsing
(GHSA-f4qm-vj5j-9xpw)
- Heap Buffer Overflow in ImageMagick MVG decoder
(GHSA-x9h5-r9v2-vcww)
- Heap overflow caused by integer overflow/wraparound in viff encoder on
32-bit builds
(GHSA-v67w-737x-v2c9)
- Stack-buffer-overflow in MNG encoder with oversized pallete
(GHSA-98cp-rj9f-6v5g)
- Integer overflow in despeckle operation causes heap buffer overflow on
32-bit builds
(GHSA-26qp-ffjh-2x4v)
- Off-by-One in MSL decoder could result in crash
(GHSA-5xg3-585r-9jh5)
- Heap buffer overflow when encoding JXL image with a 16-bit float
(GHSA-jvgr-9ph5-m8v4)
- Heap-use-after-free via XMP profile could result in a crash when
printing the values
(GHSA-r83h-crwp-3vm7)
- Heap buffer overflow (WRITE) in the YAML and JSON encoders
(GHSA-5592-p365-24xh)
- Heap out-of-bounds write in JP2 encoder
(GHSA-pwg5-6jfc-crvh)

### Library updates:
- ImageMagick 7.1.2-19 (2026-04-12)
- aom 3.13.3 (2026-04-02)
- openexr 3.4.9 (2026-04-03)
- freetype 2.14.3 (2026-03-22)
- gdk-pixbuf 2.44.6 (2026-03-31)
- harfbuzz 14.0.0 (2026-04-01)
- liblzma 5.8.3 (2026-04-31)
- libpng 1.6.56 (2026-03-25)

**Full Changelog**:
dlemstra/Magick.NET@14.11.1...14.12.0

## 14.11.1

### Related changes in ImageMagick since the last release of Magick.NET:
- Stack-buffer-overflow WRITE in InterpretImageFilename due to overflow
(GHSA-8793-7xv6-82cf)

### Library updates:
- ImageMagick 7.1.2-18 (2026-03-23)
- aom 3.13.2 (2026-03-19)
- openexr 3.4.7 (2026-03-15)
- harfbuzz 13.2.1 (2026-03-19)

**Full Changelog**:
dlemstra/Magick.NET@14.11.0...14.11.1

## 14.11.0

### What's Changed
- Added `DcmReadDefines`.

### Related changes in ImageMagick since the last release of Magick.NET:
- Access mode change for files created from 0666 to 0600
(ImageMagick/ImageMagick#8609)
- Heap-buffer-overflow in NewXMLTree could result in crash
(GHSA-gc62-2v5p-qpmp)

### Library updates:
- ImageMagick 7.1.2-17 (2026-03-16)
- openexr 3.4.6 (2026-03-01)
- freetype 2.14.2 (2026-03-01)
- harfbuzz 13.0.1 (2026-03-07)
- libxml2 2.15.2 (2026-03-03)

**Full Changelog**:
dlemstra/Magick.NET@14.10.4...14.11.0

Commits viewable in [compare
view](dlemstra/Magick.NET@14.10.4...14.12.0).
</details>

[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=Magick.NET-Q8-AnyCPU&package-manager=nuget&previous-version=14.10.4&new-version=14.12.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts page](https://github.com/dotnet/maui/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…t#35333)

Bump OpenTelemetry packages to latest stable versions in the
maui-aspire-servicedefaults template:

- OpenTelemetry.Exporter.OpenTelemetryProtocol: 1.9.0 to 1.15.3
- OpenTelemetry.Extensions.Hosting: 1.9.0 to 1.15.3
- OpenTelemetry.Instrumentation.Http: 1.9.0 to 1.15.1
- OpenTelemetry.Instrumentation.Runtime: 1.9.0 to 1.15.1

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This pull request updates the following dependencies

[marker]: <> (Begin:a71c12d9-5aa4-4b46-e2d6-08da0cf8cd95)
## From https://github.com/dotnet/xharness
- **Subscription**:
[a71c12d9-5aa4-4b46-e2d6-08da0cf8cd95](https://maestro.dot.net/subscriptions?search=a71c12d9-5aa4-4b46-e2d6-08da0cf8cd95)
- **Build**:
[20260430.4](https://dev.azure.com/dnceng/internal/_build/results?buildId=2964906)
([312724](https://maestro.dot.net/channel/2/github:dotnet:xharness/build/312724))
- **Date Produced**: May 1, 2026 7:05:11 AM UTC
- **Commit**:
[92962e5c46ac08a66ded4c5696209cc60f1a232f](dotnet/xharness@92962e5)
- **Branch**: [main](https://github.com/dotnet/xharness/tree/main)

[DependencyUpdate]: <> (Begin)

- **Dependency Updates**:
  - From [11.0.0-prerelease.26229.1 to 11.0.0-prerelease.26230.4][1]
     - Microsoft.DotNet.XHarness.CLI
     - Microsoft.DotNet.XHarness.TestRunners.Common
     - Microsoft.DotNet.XHarness.TestRunners.Xunit

[1]: dotnet/xharness@9d5a7e9...92962e5

[DependencyUpdate]: <> (End)


[marker]: <> (End:a71c12d9-5aa4-4b46-e2d6-08da0cf8cd95)

Co-authored-by: dotnet-maestro[bot] <dotnet-maestro[bot]@users.noreply.github.com>
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

## Description

Replaces `review-rules.md` (flat 345-line checklist) with a dimensional
expert review agent. Single source of truth for all review rules,
organized into 30 dimensions for per-dimension sub-agent evaluation.
Adds inline file:line PR comments alongside the existing wall-of-text
summary.

Extracted from 28k review comments across 5 maintainers via
[extraction-pipeline](https://github.com/dotnet/fsharp/blob/main/.github/agents/extraction-pipeline.md).
No functional code changes.

Recreated from dotnet#35062 on a dotnet/maui branch (originally opened from a
fork).

## What changed

**Before:** `review-rules.md` had 345 lines of flat rules. `code-review`
skill loaded them all into one context. Output was a single wall-of-text
PR comment.

**After:** Rules absorbed into `maui-expert-reviewer.md` as 30
dimensions with 200+ CHECK items. Each dimension runs as an independent
sub-agent with focused context. Output is inline file:line PR comments
via `inline-findings.json`.

## CI Flow

```
Review-PR.ps1 prompt:
  1. code-review → maui-expert-reviewer agent → inline-findings.json
  2. pr-review → Pre-Flight → Try-Fix → Report (sees findings, no duplication)

Posting:
  post-inline-review.ps1    → .json → GitHub file:line comments (NEW)
  post-ai-summary-comment.ps1 → {phase}/content.md → wall-of-text (existing)

CI: COMMENTS_VIA_FILE=true → agent writes .json, script posts
Local: agent writes .json, code-review posts directly via gh api
```

## Files

| Action | File | What |
|--------|------|------|
| **Add** | `agents/maui-expert-reviewer.md` | 30 dimensions, 200+
CHECKs, routing table |
| **Add** | `instructions/collectionview-{android,ios,windows}` |
Platform-isolated CV rules |
| **Add** |
`instructions/{handler-patterns,layout-system,performance-hotpaths,public-api,threading-async}`
| Domain-specific ambient guidance |
| **Add** | `scripts/post-inline-review.ps1` | Posts .json as GitHub PR
review |
| **Del** | `skills/code-review/references/review-rules.md` | Absorbed
into agent |
| **Mod** | `skills/code-review/SKILL.md` | Delegates to agent |
| **Mod** | `scripts/Review-PR.ps1` | Prompt + inline posting wiring |
| **Mod** | `eng/pipelines/ci-copilot.yml` | `COMMENTS_VIA_FILE` env var
|

---------

Co-authored-by: kubaflo <kubaflo@users.noreply.github.com>
Co-authored-by: T-Gro <46543583+T-Gro@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Tomas Grosup <tomasgrosup@microsoft.com>
…View2 is not connected in Appium. (dotnet#35335)

### Description of Changes

- Recently, the Appium driver has not been connecting properly to the
native WebView2 control on Windows. While running locally using Appium
Inspector with the WebView control, the inspector is unable to recognize
the WebView and displays an error.

- Due to this Appium driver issue, the WebView lane in CI takes a long
time to run (approximately 3 hours) and eventually gets cancelled. As a
temporary workaround, the WebView lane has been temporarily removed from
the Windows CI pipeline to allow the CI process to complete more
quickly.
<img width="649" height="294" alt="image"
src="https://github.com/user-attachments/assets/68df006b-56d6-4bfa-870a-a4184f5b18b7"
/>
<img width="576" height="430" alt="image"
src="https://github.com/user-attachments/assets/40c222e8-4935-450d-be7e-5ee9245e9eb1"
/>


**Issue:** dotnet#35334
@MauiBot MauiBot added s/agent-review-incomplete and removed s/agent-changes-requested AI agent recommends changes - found a better alternative or issues labels May 12, 2026
@dotnet dotnet deleted a comment from MauiBot May 12, 2026
@dotnet dotnet deleted a comment from MauiBot May 13, 2026
@dotnet dotnet deleted a comment from MauiBot May 13, 2026
@dotnet dotnet deleted a comment from MauiBot May 13, 2026
@MauiBot
Copy link
Copy Markdown
Collaborator

MauiBot commented May 13, 2026

🤖 AI Summary

👋 @BagavathiPerumal — new AI review results are available. Please review the latest session below.

📊 Review Session3c98c02 · fix-33510-Optimized code implementation · 2026-05-13 20:55 UTC
🚦 Gate — Test Before & After Fix

Gate Result: ✅ PASSED

Platform: ANDROID · Base: main · Merge base: 4f61d650

Test Without Fix (expect FAIL) With Fix (expect PASS)
🖥️ Issue33510 Issue33510 ✅ FAIL — 870s ✅ PASS — 1252s
🔴 Without fix — 🖥️ Issue33510: FAIL ✅ · 870s
  Determining projects to restore...
  Restored /home/vsts/work/1/s/src/Controls/src/BindingSourceGen/Controls.BindingSourceGen.csproj (in 502 ms).
  Restored /home/vsts/work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 6.83 sec).
  Restored /home/vsts/work/1/s/src/Controls/Maps/src/Controls.Maps.csproj (in 6.38 sec).
  Restored /home/vsts/work/1/s/src/Controls/Foldable/src/Controls.Foldable.csproj (in 169 ms).
  Restored /home/vsts/work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 8 ms).
  Restored /home/vsts/work/1/s/src/Essentials/src/Essentials.csproj (in 20 ms).
  Restored /home/vsts/work/1/s/src/Core/src/Core.csproj (in 57 ms).
  Restored /home/vsts/work/1/s/src/Core/maps/src/Maps.csproj (in 24 ms).
  Restored /home/vsts/work/1/s/src/BlazorWebView/src/Maui/Microsoft.AspNetCore.Components.WebView.Maui.csproj (in 1.48 sec).
  Restored /home/vsts/work/1/s/src/Controls/src/Xaml/Controls.Xaml.csproj (in 32 ms).
  Restored /home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj (in 1.32 sec).
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14089110
  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.14089110
  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.14089110
  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.14089110
  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.14089110
  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.14089110
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14089110
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14089110
  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.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
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14089110
  Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.Maps/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Maps.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.14089110
  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.14089110
  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.14089110
  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.14089110
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14089110
  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.14089110
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14089110
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14089110
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14089110
  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
  Controls.Xaml -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Xaml.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:09:40.53
Broadcasting: Intent { act=android.intent.action.CLOSE_SYSTEM_DIALOGS flg=0x400000 }
Broadcast completed: result=0
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.39 sec).
  Restored /home/vsts/work/1/s/src/Controls/tests/CustomAttributes/Controls.CustomAttributes.csproj (in 51 ms).
  Restored /home/vsts/work/1/s/src/TestUtils/src/VisualTestUtils.MagickNet/VisualTestUtils.MagickNet.csproj (in 6.01 sec).
  Restored /home/vsts/work/1/s/src/Controls/tests/TestCases.Android.Tests/Controls.TestCases.Android.Tests.csproj (in 7.51 sec).
  Restored /home/vsts/work/1/s/src/TestUtils/src/UITest.Core/UITest.Core.csproj (in 3 ms).
  Restored /home/vsts/work/1/s/src/TestUtils/src/UITest.Appium/UITest.Appium.csproj (in 17 ms).
  Restored /home/vsts/work/1/s/src/TestUtils/src/UITest.NUnit/UITest.NUnit.csproj (in 632 ms).
  Restored /home/vsts/work/1/s/src/TestUtils/src/UITest.Analyzers/UITest.Analyzers.csproj (in 3.63 sec).
  5 of 13 projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14089110
  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.14089110
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14089110
  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.14089110
  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
  VisualTestUtils.MagickNet -> /home/vsts/work/1/s/artifacts/bin/VisualTestUtils.MagickNet/Debug/netstandard2.0/VisualTestUtils.MagickNet.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
  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.
/home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll
[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.18]   Discovering: Controls.TestCases.Android.Tests
[xUnit.net 00:00:00.66]   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/13/2026 19:10:22 FixtureSetup for Issue33510(Android)
>>>>> 05/13/2026 19:10:27 PullToRefreshShouldNotTriggerWhenWebViewIsScrolledDown Start
>>>>> 05/13/2026 19:10:40 PullToRefreshShouldNotTriggerWhenWebViewIsScrolledDown Stop
>>>>> 05/13/2026 19:10:40 Log types: logcat, bugreport, server
  Failed PullToRefreshShouldNotTriggerWhenWebViewIsScrolledDown [14 s]
  Error Message:
     RefreshView should not trigger while WebView content is not at the top.
Assert.That(App.FindElement(StatusLabel).GetText(), Does.Not.Contain("Refresh triggered"))
  Expected: not String containing "Refresh triggered"
  But was:  "Refresh triggered"

  Stack Trace:
     at Microsoft.Maui.TestCases.Tests.Issues.Issue33510.PullToRefreshShouldNotTriggerWhenWebViewIsScrolledDown() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33510.cs:line 44

1)    at Microsoft.Maui.TestCases.Tests.Issues.Issue33510.PullToRefreshShouldNotTriggerWhenWebViewIsScrolledDown() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33510.cs:line 44


NUnit Adapter 4.5.0.0: Test execution complete

Total tests: 1
Test Run Failed.
     Failed: 1
 Total time: 1.5285 Minutes

🟢 With fix — 🖥️ Issue33510: PASS ✅ · 1252s

(truncated to last 15,000 chars)

ramework=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:02.26
* 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.14089110
  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.14089110
  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.14089110
  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.14089110
  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.14089110
  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.14089110
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14089110
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14089110
  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
  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.14089110
  Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.Maps/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Maps.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.14089110
  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.14089110
  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.14089110
  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.14089110
  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.14089110
  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.14089110
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14089110
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14089110
  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.Xaml -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Xaml.dll
  Controls.Foldable -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Foldable.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14089110
  Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Maps.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:08:29.59
Broadcasting: Intent { act=android.intent.action.CLOSE_SYSTEM_DIALOGS flg=0x400000 }
Broadcast completed: result=0
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.
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14089110
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0/Microsoft.Maui.Graphics.dll
  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.14089110
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14089110
  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.14089110
  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.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 -> /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.
/home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll
[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.13]   Discovering: Controls.TestCases.Android.Tests
[xUnit.net 00:00:00.31]   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/13/2026 19:31:22 FixtureSetup for Issue33510(Android)
>>>>> 05/13/2026 19:31:27 PullToRefreshShouldNotTriggerWhenWebViewIsScrolledDown Start
>>>>> 05/13/2026 19:31:37 PullToRefreshShouldNotTriggerWhenWebViewIsScrolledDown Stop
  Passed PullToRefreshShouldNotTriggerWhenWebViewIsScrolledDown [10 s]
NUnit Adapter 4.5.0.0: Test execution complete

Test Run Successful.
Total tests: 1
     Passed: 1
 Total time: 31.6866 Seconds

📁 Fix files reverted (6 files)
  • src/Core/src/Platform/Android/MauiHybridWebView.cs
  • src/Core/src/Platform/Android/MauiHybridWebViewClient.cs
  • src/Core/src/Platform/Android/MauiSwipeRefreshLayout.cs
  • src/Core/src/Platform/Android/MauiWebView.cs
  • src/Core/src/Platform/Android/MauiWebViewClient.cs
  • src/Core/src/PublicAPI/net-android/PublicAPI.Unshipped.txt

New files (not reverted):

  • src/Core/src/Platform/Android/RefreshViewWebViewScrollCapture.cs

🧪 UI Tests — RefreshView,ViewBaseTests

Detected UI test categories: RefreshView,ViewBaseTests

Deep UI tests — 198 passed, 0 failed across 2 categories on platform-pool agent (replaces in-process counts above).

🧪 UI Test Execution Results (deep, platform pool)

Category Tests Snapshot diffs
controls-RefreshView 40/40 ✓
controls-ViewBaseTests 158/159 ✓
📎 Download drop-deep-uitests artifact (TRX + snapshot diffs)

🔍 Pre-Flight — Context & Validation

Issue: #33510 — [Android] RefreshView triggers pull-to-refresh immediately when scrolling up inside a WebView
PR: #34614 — [Android] Fix for RefreshView triggering pull-to-refresh when scrolling inside a WebView with internal scrollable content
Platforms Affected: Android
Files Changed: 7 implementation, 2 test (647 additions, 1 deletion)

Problem Summary

Android RefreshView wraps a WebView. When the WebView shows web content scrolled by an internal HTML container (overflow-y: auto div), the native WebView.ScrollY / CanScrollVertically(-1) may report the WebView itself is at the top while the inner DOM element is mid-scroll. SwipeRefreshLayout.canChildScrollUp() therefore returns false and intercepts the upward swipe as pull-to-refresh, breaking inner scroll.

PR's Fix Architecture

  1. JS-bridge observer (RefreshViewWebViewScrollCapture.cs, 228 LOC): An [JavascriptInterface] named mauiRefreshViewHost is injected into the WebView when (and only when) it is inside a MauiSwipeRefreshLayout. A capture-phase touchstart/touchmove listener walks the touched DOM element's ancestors to find the nearest scrollable element, and reports its scrollTop > 0 to native via setCanScrollUp(bool).
  2. MauiWebView / MauiHybridWebView: OnAttachedToWindow calls Attach(this) and re-injects the observer for late-attach. OnDetachedFromWindow/Dispose calls Detach. MauiWebViewClient.OnPageStarted resets state; OnPageFinished re-injects observer (gated by IsAttached).
  3. MauiSwipeRefreshLayout: New OnInterceptTouchEvent override hit-tests the touch position to find the WebView, snapshots ScrollCaptureState on ACTION_DOWN, and forwards/blocks intercept based on the cached CanScrollUp flag (volatile, no JNI per-frame). On ACTION_MOVE, releases ownership back to RefreshView once inner scroll reaches the top mid-gesture. Multi-touch (POINTER_DOWN) cancels the guard.
  4. State lifecycle: ScrollCaptureState : Java.Lang.Object uses volatile bool _detached so in-flight JNI callbacks become no-ops after detach instead of touching disposed objects. MarkDetached runs before RemoveJavascriptInterface.

Key Findings

  • The PR's approach is functionally necessary because inner DOM scroll is not observable from the native side without JS — there is no Android API that reports CSS overflow-y scroll position.
  • Heavyweight: 647 LOC across 7 files; introduces a JS bridge, capture-phase event listeners, hit-testing, and a parallel touch-intercept state machine in MauiSwipeRefreshLayout.
  • Adds 7 new public API surface entries (MauiWebView.OnDetachedFromWindow, MauiHybridWebView.OnAttachedToWindow/OnDetachedFromWindow/Dispose, MauiHybridWebViewClient.OnPageStarted/OnPageFinished, MauiSwipeRefreshLayout.OnInterceptTouchEvent).
  • Inline review thread feedback already addressed iteratively: gating injection so it only runs when inside a RefreshView, mid-gesture re-evaluation on ACTION_MOVE, ScrollX/ScrollY correction in hit-test, deferred GC for ScrollCaptureState, late-attach re-injection, named JS handlers to support Detach/re-attach.
  • Gate already verified ✅: test fails without fix, passes with fix.

Code Review Summary

Verdict: NEEDS_DISCUSSION (alternative approaches worth exploring)
Confidence: medium
Errors: 0 | Warnings: 2 | Suggestions: 3

Key code review findings:

  • ⚠️ Public API surface increase (7 entries) — large surface for a niche fix. Could mark some as internal overrides where possible.
  • ⚠️ Capture-phase touchstart/touchmove listeners on every page load run JS on every user touch, even outside the RefreshView gesture window. Performance impact unmeasured.
  • 💡 evaluateJavascript of ObserverScript (~80 lines minified) on every OnPageFinished may delay JS execution on slow devices.
  • 💡 MauiHybridWebView inherits the same fix path — consistent coverage, but doubles the attack surface for regression.
  • 💡 FindWebView recurses on every OnInterceptTouchEvent — fine for typical layouts, but O(N) per gesture frame on ACTION_DOWN only (acceptable).

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #34614 Persistent JS observer with capture-phase touch listeners + native touch-intercept state machine ✅ Gate PASSED 7 impl, 2 test Original PR — heavyweight (647 LOC), large public API
1 try-fix-1 NestedScrolling delegation: override MauiWebView.OnTouchEvent to call Parent.RequestDisallowInterceptTouchEvent on ACTION_DOWN and release on UP based on ScrollY deltas, no JS See try-fix-1 1–2 impl Limitation: cannot detect inner-DOM-only scroll
2 try-fix-2 On-demand JS evaluation only at gesture start (evaluateJavascript callback in OnInterceptTouchEvent) — no persistent observer, no touch listeners See try-fix-2 2–3 impl Async result arrives mid-gesture; needs short defer window
3 try-fix-3 Minimal JS observer that only updates a single window.__mauiCanScrollUp value via scroll/touchstart (no native callback per move) + native polls via evaluateJavascript once on ACTION_DOWN See try-fix-3 3–4 impl Reduced JS overhead vs PR; still needs JS bridge for sync read

🔧 Fix — Analysis & Comparison

try-fix aggregate: alternatives to PR #34614

PR #34614 fixes #33510 (Android RefreshView + WebView with inner-DOM
overflow-y scroll triggers spurious pull-to-refresh) by installing a persistent
JS observer
in every WebView hosted inside a RefreshView. The observer registers
capture-phase touchstart/touchmove listeners that walk to the nearest CSS-scrollable
ancestor and push setCanScrollUp(scrollTop > 0) across a [JavascriptInterface]-decorated
Java.Lang.Object (ScrollCaptureState) into native code; MauiSwipeRefreshLayout
then reads a volatile bool per ACTION_MOVE.

This document compares three architecturally distinct alternatives.

Comparison table

Criterion PR (persistent JS push) try-fix-1: Native NestedScrolling try-fix-2: On-demand JS at DOWN try-fix-3: Minimal JS flag + native poll
Fixes inner-DOM scroll bug ❌ (no native signal for CSS overflow scroll)
Pull-to-refresh still works at top ⚠️ (race on cold V8) ⚠️ (race on first gesture after late-attach)
No regression for non-WebView children
Covers MauiHybridWebView
Survives Shell tab-switch ✅ (Detach+re-Attach + re-inject) ✅ (no shared state) ✅ (no persistent state) ✅ (idempotent script)
Late-attach safe
GC / JNI-callback safe ⚠️ (manual MarkDetached + volatile fields + "do NOT Dispose because V8 may hold ref") ✅ (no JNI bridge) ✅ (transient IValueCallback only) ✅ (transient IValueCallback only)
Public API surface +7 unshipped −7 (smaller) −7 (smaller) −7 (smaller)
JS / runtime overhead every touch frame, page-lifetime listeners + JNI hop zero JS one EvaluateJavascript per ACTION_DOWN, no listeners two listeners (write primitive only) + one EvaluateJavascript per ACTION_DOWN
Gate test (Issue33510) — analytical PASS FAIL PARTIAL → likely PASS PASS
LOC delta vs main +780 ~+25 (huge deletion) ~+90 (large deletion) ~+110 (moderate deletion)

Recommendation

Stick with the PR's approach. The PR has the strongest correctness guarantee on
the gate test — it has the answer ready before ACTION_DOWN fires, so it cannot
miss a fast first-touch flick the way candidates B and C can on cold V8. Its main
downsides (the ScrollCaptureState : Java.Lang.Object lifecycle, +7 public API,
per-touch JNI) are all real but are correctly mitigated in the PR (volatile

  • MarkDetached + idempotent script + capped JNI to a single boolean write).

However, if reviewers want to reduce the API/JNI surface area without losing fix
quality, try-fix-3 is the next-best option: it preserves the push-based DOM
observation so the answer is current (no per-touch DOM walk like candidate B), but
deletes the [JavascriptInterface] bridge and all 7 unshipped public-API entries.
The single residual gap — the very first gesture after late-attach into a never-touched
inner div — is narrower than the steady-state cost of the PR's persistent JNI bridge,
and recompute(document.body) at install time covers the document-scroll case.

try-fix-1 should not be merged on its own (does not fix the bug), but it is a
useful demonstration of why the PR's complexity exists: Android exposes no native
signal for inner CSS-overflow scroll, so any honest fix must consult the JS context
in some form.

Honest evaluation note

Android device-test execution was not possible in this Linux build agent (gate
already PASSED for the PR's existing fix). All candidate evaluations above are
analytical against the semantics of the gate test in Issue33510.cs and the
acceptance criteria stated in the task. The races in candidates B and C are real but
narrow (typically 5–30 ms on warm V8) and would only manifest as a single missed
pull-to-refresh on the very first fast flick — never as the false-positive symptom
that issue #33510 reports. None of the candidates introduce a regression worse than
the bug they are meant to fix.


📋 Report — Final Recommendation

PR #34614 — Comparative Candidate Report

Issue: #33510 — [Android] RefreshView triggers pull-to-refresh immediately when scrolling up inside a WebView with inner CSS-overflow content.
Platform under test: Android.
Gate: ✅ already PASSED for the PR's existing fix (test fails without fix, passes with fix). Not re-run.

Candidates evaluated

# Candidate Source Approach Public API delta Gate Build Notes
1 pr PR #34614 as submitted Persistent JS observer ([JavascriptInterface] ScrollCaptureState : Java.Lang.Object) + capture-phase touchstart/touchmove listeners + native OnInterceptTouchEvent state machine in MauiSwipeRefreshLayout. +7 unshipped ✅ PASS Heavyweight (647 LOC). Has the correctness answer ready before ACTION_DOWN.
2 pr-plus-reviewer pr + maui-expert-reviewer feedback Same architecture as PR. Reviewer additionally: clears cached gesture refs in MauiSwipeRefreshLayout.OnDetachedFromWindow (closes a Shell-tab-switch GREF retention window), adds a positive-case test, sorts PublicAPI.Unshipped.txt alphabetically. +7 unshipped (sorted) ✅ PASS (analytical — additive only) Strict superset of pr. No behavior change on the original repro; closes regression-test gap and a small memory-hygiene issue.
3 try-fix-1 Native NestedScrolling delegation in MauiWebView.OnTouchEvent (no JS, no OnInterceptTouchEvent override). None FAIL (analytical) Cannot detect inner-DOM CSS-overflow scroll — Android exposes no native signal for it. Does not fix #33510.
4 try-fix-2 On-demand evaluateJavascript from OnInterceptTouchEvent ACTION_DOWN, no persistent observer. None ⚠️ Partial (likely PASS) Async result arrives mid-gesture; race window vs first fast flick. Removes JNI bridge & 7 API entries.
5 try-fix-3 Minimal JS observer that writes window.__mauiCanScrollUp only (no JavascriptInterface); native polls once per ACTION_DOWN via evaluateJavascript. −7 (removes all PR-added entries) ✅ PASS (analytical) Hybrid: push-based JS freshness + on-demand transport. Eliminates entire Java.Lang.Object/V8-GC class of bugs that the PR explicitly works around.

Ranking criteria

Per task constraint: candidates that fail regression tests rank below candidates that pass. Within the passing tier, ranked on: (a) correctness on edge cases, (b) lifecycle / GC safety, (c) API surface / blast radius, (d) test quality.

Ranking

Rank Candidate Tier Rationale
1 pr-plus-reviewer PASS Strict superset of pr. Closes the only material regression-test gap (no positive-case assertion) and a small Shell-tab-switch GREF retention window. Same correctness profile as pr on the gate.
2 pr PASS Strongest correctness on the gate test — answer is ready before ACTION_DOWN, no race on first flick. Mitigations for JS-bridge lifetime are present and correctly ordered (MarkDetached before RemoveJavascriptInterface).
3 try-fix-3 PASS Materially smaller API/JNI surface. Removes the PR's most fragile piece (ScrollCaptureState : Java.Lang.Object with explicit "do NOT Dispose" comment). Slightly more brittle on first late-attach gesture (mitigated by recompute(document.body) at install).
4 try-fix-2 PARTIAL Same async-callback race but without a pre-computed JS-side flag — the DOM walk happens during the race window. Higher risk of missed first-flick than try-fix-3.
5 try-fix-1 FAIL Does not fix the bug. Useful only as proof that any honest fix must consult JS. Ranked last per the "failed candidates rank lower" rule.

Why pr-plus-reviewer wins over pr

The reviewer's applied changes are:

  1. Test gap closed. A new PullToRefreshShouldTriggerWhenWebViewIsAtTop test asserts the positive case. Without it, a regression that causes MauiSwipeRefreshLayout to always block pull-to-refresh inside a WebView would still pass the existing test.
  2. OnDetachedFromWindow clears _activeTouchWebView / _activeTouchScrollState. Closes a Shell-tab-switch window where the refresh layout's stale Java.Lang.Object reference can keep the WebView's GREF alive past expected lifetime.
  3. PublicAPI.Unshipped.txt sorted alphabetically — consistency with repo convention.

These are additive and non-controversial. They preserve the PR's gate-passing behavior (the changes don't touch the touch state machine or the JS bridge) and they ship a strictly more defensible test surface. Build verified ✅ on net10.0-android36.0.

Why not try-fix-3 over pr-plus-reviewer

try-fix-3 is architecturally cleaner and removes 7 unshipped public-API entries plus the ScrollCaptureState : Java.Lang.Object lifetime hazard. It is the strongest alternative design. However:

  • The PR has already shipped the heavyweight design through multiple inline-review iterations (gating, mid-gesture re-eval, late-attach, idempotent injection). That review work is sunk cost only if we ship something else.
  • try-fix-3 keeps a small but real first-late-attach race that the PR does not have.
  • All try-fix-* candidates were evaluated analytically only — no Android device available on this Linux build agent. Replacing the PR's gate-verified fix with an analytical-only redesign on a release-affecting bug is a worse risk profile than shipping the PR with reviewer touch-ups.
  • The reviewer correctly notes that the +7 public API entries are not avoidable by changing visibility — Java-base virtuals (OnAttachedToWindow, OnDetachedFromWindow, OnPageStarted, OnPageFinished, OnInterceptTouchEvent) require public accessibility from C# wrappers.

If a follow-up cleanup PR wants to migrate to try-fix-3's simpler model later, the PR's tests will still serve as regression coverage. That refactor should not block this fix.

Inline reviewer findings

Raw findings written to CustomAgentLogsTmp/PRState/34614/PRAgent/inline-findings.json (16 items: 0 errors, 3 warnings, ~9 suggestions, 4 nits). Headline items:

  • W: Test only asserts negative case (test gap — addressed in pr-plus-reviewer).
  • W: OnInterceptTouchEvent does not handle MotionEventActions.PointerUp — multi-touch state-machine edge case.
  • W: FindWebView ignores TranslationX/Y; should use View.GetHitRect for transform-aware hit-test.
  • S: Capture-phase JS listeners run on every touch (pre-flight W2 confirmed) — left for human discussion.
  • S: InjectObserver runs on every OnPageFinished — pre-flight S1 confirmed.
  • S: Comment-only nits on CanScrollUpViewByType WebView fall-through and discarded base.OnInterceptTouchEvent return.

Pre-flight W1 (public-API growth) is reclassified as moot: the entries are mandatory because the C# overrides wrap Java public virtuals.

Honest evaluation note

Android device-test execution is not feasible on this Linux build agent. Gate already PASSED for the PR's existing fix. pr-plus-reviewer is evaluated as a strict additive superset of pr and verified to build on net10.0-android36.0. All try-fix-* candidates are evaluated analytically against the gate's semantics in Issue33510.cs. The races flagged for try-fix-2/try-fix-3 are real but narrow (5–30 ms on warm V8) and would manifest as a single missed pull-to-refresh on a fast first flick — never as the false-positive symptom that issue #33510 reports.

Winner

pr-plus-reviewer — apply the reviewer's three additive touch-ups on top of the PR. isPRFix = true.


@kubaflo kubaflo changed the base branch from main to inflight/current May 14, 2026 07:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community ✨ Community Contribution partner/syncfusion Issues / PR's with Syncfusion collaboration platform/android s/agent-changes-requested AI agent recommends changes - found a better alternative or issues s/agent-fix-win AI found a better alternative fix than the PR s/agent-gate-passed AI verified tests catch the bug (fail without fix, pass with fix) s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Android] RefreshView triggers pull-to-refresh immediately when scrolling up inside a WebView

10 participants