Repository navigation
fix(android): read supplemental description so WebView 150+ inputs stay matchable - #3498
Open
Timbological wants to merge 2 commits into
Open
Timbological wants to merge 2 commits into
Timbological wants to merge 2 commits into
Conversation
1 task done
Contributor
|
Can you reduce kdoc? It's a little much. |
Author
|
Trimmed — thanks. The Chromium flag/call-site detail is all in #3497, so I've cut it from the code and left one line at each site plus the reason for the empty-name guard in |
This comment was marked as outdated.
This comment was marked as outdated.
Fishbowler
force-pushed
the
fix/webview-150-supplemental-description
branch
2 times, most recently
from
August 21, 2026 14:59
3e80085 to
f89516f
Compare
Fishbowler
reviewed
Aug 24, 2026
Comment on lines
+94
to
+99
| // Only when the node has no other name: a supplemental description is documented | ||
| // as purely supplemental, but on WebView 150+ an input's name arrives only here. | ||
| if (!it.attributes["text"].isNullOrEmpty() | ||
| || !it.attributes["hintText"].isNullOrEmpty() | ||
| || !it.attributes["accessibilityText"].isNullOrEmpty() | ||
| ) return@filter false |
Contributor
There was a problem hiding this comment.
Is this necessary? This check should be inert, since we expect the others to be empty. But if it weren't, wouldn't a user expect matching to be simpler, rather than special cased?
Fishbowler
force-pushed
the
fix/webview-150-supplemental-description
branch
from
August 25, 2026 08:17
121e4d1 to
84f49c8
Compare
Fishbowler
force-pushed
the
fix/webview-150-supplemental-description
branch
2 times, most recently
from
September 15, 2026 16:25
92c4006 to
6998f5c
Compare
…ay matchable From WebView 150 (Chromium M150) the accessible name of a web text input is no longer folded into hintText: kAccessibilityPopulateSupplementalDescriptionApi became enabled by default, which skips the branch of BrowserAccessibilityAndroid::GetAndroidHint() that used to push the name into the hint. Chromium delivers it through AccessibilityNodeInfoCompat.setSupplementalDescription() instead — natively on API 36, and in the node's extras below that. The Android driver reads only text, hintText and contentDescription, so every WebView <input> now arrives nameless and tapOn/assertVisible on its placeholder, aria-label or <label for> fails with "Element not found" for a field that is plainly visible and correctly labelled. Buttons are unaffected, since their names ride on text/contentDescription. Bisected across six WebView builds with a single unchanged APK: 124, 133, 145, 147 and 149 all pass; 150.0.7871.46 and 152.0.7977.30 fail. Serialise the value as a supplementalDescription attribute, carry it through mapHierarchy's attribute allowlist, and consult it in textMatches only when text, hintText and accessibilityText are all empty — a supplemental description is documented as purely supplemental, so it must not outrank a real name, but on WebView 150+ an input's name exists nowhere else. Refs mobile-dev-inc#3497
Cut the Chromium flag and call-site detail from the KDoc and inline comments — that archaeology lives in the issue. What stays is what a reader needs in place: WebView 150+ puts a web text input's name here instead of in hintText, and why the match consults it only when no other name is present. Refs mobile-dev-inc#3497
Fishbowler
force-pushed
the
fix/webview-150-supplemental-description
branch
from
October 16, 2026 16:23
6998f5c to
1311b11
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Proposed changes
Fixes the WebView 150+ regression reported in #3497: every text input inside a WebView is
unmatchable by name, so
tapOn/assertVisibleon a field's placeholder,aria-labelor<label for>fails withElement not foundfor an element that is plainly visible and correctlylabelled.
Cause is upstream and intentional. Chromium M150 flipped one flag's default in
content/public/common/content_features.cc:BASE_FEATURE(kAccessibilityPopulateSupplementalDescriptionApi, base::FEATURE_DISABLED_BY_DEFAULT);BASE_FEATURE(kAccessibilityPopulateSupplementalDescriptionApi, base::FEATURE_ENABLED_BY_DEFAULT);BrowserAccessibilityAndroid::GetAndroidHint()is byte-identical between the two builds and isgated on that flag (browser_accessibility_android.cc#L1139):
With the flag on, the name is no longer folded into
hintText; it goes toAccessibilityNodeInfoCompat.setSupplementalDescription()instead — natively on API 36, in thenode's
extrasbelow that. A text input's name lives only there, which is why inputs break andbuttons don't (their names ride on
text/contentDescription). Screen readers are unaffected, sothis is specific to consumers reading raw
AccessibilityNodeInfofields.Four edits, because a device-side change alone is silently dropped:
AccessibilityNodeInfoExt.kt— read the value, native getter on API 36+ with the AndroidXextras key as the pre-36 fallback.
ViewHierarchy.kt— serialise it as asupplementalDescriptionattribute, besidehintText.AndroidDriver.kt— carry it throughmapHierarchy, whose attribute allowlist otherwise dropsanything new even though the device emitted it.
Filters.kt— add it to thetextMatches/hintTextMatches/accessibilityTextMatchesunion,guarded to fire only when
text,hintTextandaccessibilityTextare all empty. AndroidXdocuments a supplemental description as "purely supplemental", so it must never outrank a real
name; the guard keeps that honest while making nameless-everywhere-else inputs matchable.
I've left the tracked
maestro-app.apk/maestro-server.apkalone, sinceupdate-drivers.yamlrebuilds them on
mainwith the canonical toolchain after merge — happy to commit rebuiltartifacts instead if you'd rather.
Testing
Bisected with a single unchanged APK on one device, swapping only the WebView provider, so the
WebView version is the only variable:
tapOnby labelUpstream checks:
./gradlew test detektand./gradlew detektMainare BUILD SUCCESSFUL onthis branch (all module unit tests plus
:maestro-testintegration tests).A/B/A control at the identical commit. Control CLI built from a
git worktreeat the samecommit with a clean
git status, so the only difference ismaestro-client.jar(which carries boththe client change and the bundled driver APK). Verified unpatched: 0 of its dex files contain
supplementalDescription, against exactly 1 (classes5.dex) in this build. Both report2.8.0.Same emulator (API 35, WebView 150.0.7871.46), same app, same flow:
Because the run was on API 35, the value came from the AndroidX extras Bundle rather than the
native API-36 getter — the compatibility fallback is exercised.
Probes (bare HTML, no framework) — every labelling mechanism, nameless on 150 before this change:
Suite-level regression check. Ten flows from a real suite, each run under both CLIs back to back
on the same WebView-150 emulator. Six touch the search field by name; four never do (controls).
Recording the first failing step rather than just pass/fail, because some flows in that suite are
independently red on stale assertions — under stock they die at the search field before reaching
them.
search-filtersTap on "Search"back-navigationAssert "Search" visiblehomeAssert "Search" visibleAssert "Select a Region" visiblenavigationAssert "Search" visibleAssert "Select a Region" visibledark-themeAssert "Search" visibleAssert "Select a Region" visibleorientationAssert "Search" visibleAssert "Select a Region" visibleabout(control)deep-link-offline(control)deep-link-qr(control)Assert "Spanish" visibleAssert "Spanish" visibledeep-link-legacy-resume(control)Assert "Spanish" visibleAssert "Spanish" visibleNo flow regressed: two went red → green, four advanced from the search field to a later
pre-existing failure of that suite's own making (
Select a Regionis stale text the app no longerrenders), and all four controls are identical — including the two already failing for their own
reasons.
Blast radius is small, and the change restores rather than invents matches. On a real screen of
279 nodes only 2 carry a supplemental description and just 1 becomes newly matchable — the
android.webkit.WebViewroot, whose name is the document title. Stock Maestro on WebView 145already reported it as
{'text': '5fish', 'class': 'android.webkit.WebView'}; on 150 it isnameless; with this change it is matchable again. (Worth noting the regression isn't limited to
inputs: the WebView root's title moved too, so
assertVisible: "<page title>"also broke silentlyon 150.) Pre-150 the change is inert — across 3 screens and 221 nodes on WebView 145, zero nodes
carry a supplemental description. No measurable cost: 3130 ms per
hierarchydump on this branchvs 3211 ms stock (mean of 3, same 279-node screen).
iOS and web are unaffected despite
Filters.ktbeing shared —IOSDriverpopulateshintTextfrom
placeholderValueand nothing setssupplementalDescription, so the new branch never fires.On e2e tests: I haven't added one, because reproducing this needs a device with WebView 150+
and the Android CI image pins its WebView (a
google_apisimage has no Play Store, so it stays atwhatever the image bundles — 133 for
android-36). That pinning is also why this regression isinvisible to CI while every developer device auto-updates past 150 and fails. Glad to add an
e2e/demo_appscreen and flow if you can point me at a runner where the WebView version can beraised.
Issues fixed
Refs #3497. Same underlying cause as #3435, which was closed as
not_plannedon the premise thatthe name isn't in the tree — it is, it just moved to a field the driver doesn't read. (That
reporter's own data had the answer: WebView
150.0.7871.46failing vs149.0.7827.160working;they discarded the version hypothesis because the OS versions ran the other way.)