Skip to content

[PM-41294] feat: Build and fill identity autofill partitions through a multi-partition pipeline - #7369

Open
aj-rosado wants to merge 6 commits into
PM-41293/identity-autofill-fill-assist-field-mappingfrom
PM-41294/identity-autofill-multi-partition-pipeline
Open

[PM-41294] feat: Build and fill identity autofill partitions through a multi-partition pipeline#7369
aj-rosado wants to merge 6 commits into
PM-41293/identity-autofill-fill-assist-field-mappingfrom
PM-41294/identity-autofill-multi-partition-pipeline

Conversation

@aj-rosado

Copy link
Copy Markdown
Contributor

🎟️ Tracking

https://bitwarden.atlassian.net/browse/PM-41294

📔 Objective

Adds Identity autofill, behind FlagKey.IdentityAutofill:

  • AutofillParserImpl.kt — builds and fills a real AutofillPartition.Identity.
  • ViewNodeExtensions.kt — heuristic detection of identity fields (name, address, phone, etc.), classified alongside Login/Card.
  • FillAssistViewNodeExtensions.kt — fill-assist targeting rule support for identity field keys.
  • FilledDataBuilderImpl.kt — fills the Identity partition from the vault.

📸 Screenshots

N/A — no UI changes.

@aj-rosado aj-rosado added the ai-review Request a Claude code review label Sep 9, 2026
@github-actions github-actions Bot added app:password-manager Bitwarden Password Manager app context t:feature Change Type - Feature Development labels Sep 9, 2026
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

🤖 Bitwarden Claude Code Review

Overall Assessment: APPROVE

Reviewed at 73d3e9e25 — the same head as the previous round, so this pass re-verified the whole diff rather than an increment. The change moves partition building in FilledDataBuilderImpl into buildFilledPartitions and gives AutofillPartition.Identity a real implementation, lets AutofillParserImpl construct an Identity partition via the new effectiveFocusedViewOrNull precedence (focused non-Identity → focused any → non-Identity → any), reclassifies phone fields to Login.Username with an Identity.PhoneFull dual-classification sibling, and reworks the traverse duplicate-AutofillId filter so a sibling from the same child survives while a container-redirect leftover claimed by an ancestor is dropped.

I traced each changed path against the FlagKey.IdentityAutofill gate and confirmed the flag-off behavior is preserved: View.AUTOFILL_HINT_PHONE sits in SUPPORTED_IDENTITY_VIEW_HINTS (consulted only when the flag is on), isPhoneField -> AutofillHint.Login.USERNAME sits below the !isIdentityAutofillEnabled -> null guard, the new "phone" entry in SUPPORTED_RAW_PHONE_HINTS only reaches flag-gated call sites, and the Unused/non-Identity branches of the rewritten dedup filter are semantically identical to the code they replace. All checks are green, including Test static-analysis and Lint.

Code Review Details

No new findings in this round.

Notes on the three existing threads, for the record:

  • AutofillParserImpl.kt:211 — the vault-item dataset still maps Identity to AutofillSelectionData.Type.LOGIN. Deferred by the author to a follow-up branch; still present at this head and still reachable only with the flag on.
  • AutofillParserImpl.kt:277 — with the flag on, loginCategories drops account-creation/account-update, removing Login fill-assist coverage on those hosts. Also deferred; the flag-off branch keeps today's coverage, so nothing ships changed here.
  • HtmlInfoExtensions.kt:272 — the "autocomplete" question is closed and that file is no longer in this PR's diff.

Both deferred items need to land before pm-38138-mobile-identity-autofill is enabled anywhere, but neither blocks this merge.

@codecov

codecov Bot commented Sep 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 79.00000% with 21 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.20%. Comparing base (9d4563d) to head (73d3e9e).

Files with missing lines Patch % Lines
...den/data/autofill/builder/FilledDataBuilderImpl.kt 76.56% 8 Missing and 7 partials ⚠️
...twarden/data/autofill/parser/AutofillParserImpl.kt 86.20% 0 Missing and 4 partials ⚠️
...bitwarden/data/autofill/util/ViewNodeExtensions.kt 66.66% 0 Missing and 2 partials ⚠️
Additional details and impacted files
@@                                   Coverage Diff                                    @@
##           PM-41293/identity-autofill-fill-assist-field-mapping    #7369      +/-   ##
========================================================================================
- Coverage                                                 86.41%   86.20%   -0.21%     
========================================================================================
  Files                                                       930      946      +16     
  Lines                                                     67345    67967     +622     
  Branches                                                  10197    10253      +56     
========================================================================================
+ Hits                                                      58198    58594     +396     
- Misses                                                     5567     5774     +207     
- Partials                                                   3580     3599      +19     
Flag Coverage Δ
app-data 18.16% <79.00%> (-0.10%) ⬇️
app-ui-auth-tools 18.79% <0.00%> (+0.17%) ⬆️
app-ui-platform 16.42% <0.00%> (+0.16%) ⬆️
app-ui-vault 27.70% <0.00%> (+0.09%) ⬆️
authenticator 5.99% <0.00%> (-0.02%) ⬇️
lib-core-network-bridge 4.03% <0.00%> (-0.01%) ⬇️
lib-data-ui 1.18% <0.00%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

// Gated behind FlagKey.IdentityAutofill until the feature is ready for
// production; disabled matches this partition's pre-feature behavior.
if (!isIdentityAutofillEnabled) return AutofillRequest.Unfillable
AutofillPartition.Identity(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ IMPORTANT: Identity partitions become constructible here, but the vault-item dataset still maps them to Type.LOGIN.

Details and fix

FilledDataExtensions.kt:37-43 maps AutofillPartition.Identity to AutofillSelectionData.Type.LOGIN, justified by the comment "no-op placeholder today since nothing yet produces an Identity-only partition". This PR invalidates that premise, and nothing in the diff updates it.

Execution path once the flag is on and an Identity partition is built:

  1. FillResponseBuilderImpl.build unconditionally adds filledData.buildVaultItemDataset(...).
  2. That intent carries Type.LOGIN, which AutofillSelectionDataExtensions maps to VaultItemListingType.Login, so the "My vault" entry opens a Login-only item list on an identity form.
  3. Selecting a login there runs AutofillCompletionManagerImpl, which reparses the structure (still an Identity partition) and builds against cipherView.toAutofillCipherProvider(). getIdentityAutofillCiphers() returns emptyList() for a login cipher, so filledPartitions.firstOrNull() is null and the activity cancels with "Dataset not found" — nothing is filled.

When the vault is locked, getIdentityAutofillCiphers() returns empty, so the vault-item dataset is the only entry offered, and this is the whole identity-autofill experience for a locked vault.

Suggested fix: add an IDENTITY selection type (or omit the vault-item dataset for AutofillPartition.Identity while that type does not exist) so the fallback entry cannot route the user to a list that can never fulfil the request.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@claude this will be addressed in a future branch. Until the feature is fully ready feature flag won't be active

@claude claude Bot Sep 9, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Claude finished @aj-rosado's task in 1m 4s —— View job


Response

Understood — thanks for the context. Deferring the two flag-on findings to a follow-up branch makes sense given FlagKey.IdentityAutofill stays off through this rollout:

  • AutofillParserImpl.kt:211 — vault-item dataset mapping Identity → Type.LOGIN (flag-gated, deferred)
  • AutofillParserImpl.kt:277 — Login fill-assist coverage on account-creation/account-update hosts (flag-gated, deferred)
  • HtmlInfoExtensions.kt:272not flag-gated

One item the flag doesn't cover

The "autocomplete" removal from SUPPORTED_HTML_ATTRIBUTE_HINTS ships to production with the flag off:

private val SUPPORTED_HTML_ATTRIBUTE_HINTS: List<String> = listOf(
    "name",
    "label",
    "type",
    "hint",
    "autofill",
)

hints() is called unconditionally from the HtmlInfo heuristics — isUsernameField(), isPasswordField(), and the card patterns all read the same values, and none of those paths check FlagKey.IdentityAutofill. containsAnyTerms matches on the attribute name, and "autocomplete" doesn't contain "autofill", so autocomplete="email" / "cc-number" / "new-password" on a web form now contributes no hint at all. That's a behavior change to Login and Card detection today, regardless of the flag.

It may still be the right call — if some autocomplete values were misclassifying identity fields, dropping the attribute is one way to fix it. Two things would help:

  1. A line in the PR description saying it's a deliberate revert of the autocomplete term added in d23d1d17, and why.
  2. Consider IGNORED_RAW_HINTS for the specific offending values instead, so the standard web attribute keeps contributing where it's correct. This file has no unit coverage, so a regression here wouldn't be caught by CI.

If it is intentional and scoped as-is, feel free to resolve — just wanted to make sure it wasn't swept in under the flag-off assumption alongside the other two.

Fix this →


PM-41294/identity-autofill-multi-partition-pipeline

Comment on lines +273 to +277
val loginCategories = if (isIdentityAutofillEnabled) {
LOGIN_FILL_ASSIST_CATEGORIES
} else {
LOGIN_FILL_ASSIST_CATEGORIES + IDENTITY_FILL_ASSIST_CATEGORIES
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ IMPORTANT: Turning the flag on removes fill-assist coverage for Login fields on account-creation/account-update hosts.

Details and fix

With isIdentityAutofillEnabled == true, loginCategories is just account-login. So when focusedView is an AutofillView.Login on a host whose rules are categorized only account-creation or account-update, coversCurrentPartition is false and the curated selectors are discarded in favour of heuristics — a regression against today's behavior, where both categories are Login categories.

Those categories carry Login field keys: FIELD_KEY_NEW_PASSWORD ("newPassword") only makes sense on a signup or change-password flow. So on a registration or account-settings host, focusing the password or username field is exactly where the authored rules were meant to apply, and is exactly where they now stop applying. The category describes the page flow, not the field type.

Suggested fix: keep the identity categories additive for the Login branch in both flag states (LOGIN_FILL_ASSIST_CATEGORIES + IDENTITY_FILL_ASSIST_CATEGORIES); the new is AutofillView.Identity branch already claims these categories when an identity field is what has focus, and effectiveFocusedViewOrNull keeps Login authoritative afterwards.

Note the new tests cover the flag-off case for this shape (...account-creation and a login view is focused with IdentityAutofill disabled) but not the flag-on case.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@claude this will be addressed in a future branch. Until the feature is fully ready feature flag won't be active

@claude claude Bot Sep 9, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Claude finished @aj-rosado's task in 1m 19s —— View job


Understood — deferring the vault-item dataset routing

Thanks @aj-rosado, that disposition makes sense for that finding. AutofillPartition.Identity is only reachable when FlagKey.IdentityAutofill is on (AutofillParserImpl.kt:210 returns Unfillable otherwise), so with the flag inactive the Type.LOGIN mapping in FilledDataExtensions.kt:37-43 stays the no-op its comment describes. Nothing to change in this PR — worth carrying into the follow-up ticket so the placeholder comment gets retired alongside the new selection type.

The same reasoning covers my second comment (fill-assist categories on account-creation/account-update): the narrowing only takes effect with the flag on, so it can ride the same follow-up. One thing to note for when the flag flips — that one is a regression against today's shipped behavior rather than an incomplete new path, so it's easy to lose track of. If you'd rather not carry it, keeping the categories additive in both branches is a two-line change and the new is AutofillView.Identity branch already handles identity-focused fields.

Still open: the autocomplete removal is not flag-gated

This one isn't covered by the flag argument, so it's worth a decision before merge.

SUPPORTED_HTML_ATTRIBUTE_HINTS at HtmlInfoExtensions.kt:267-273 contains "autocomplete" on main (present at b1d6d6b); the first commit on this branch (f926f4c) drops it, and it's still absent at HEAD. hints() feeds every HtmlInfo heuristic — isPasswordField(), isUsernameField(), all seven card predicates — none of which consult the flag. So on web forms, autocomplete="new-password", "username", "cc-number" etc. stop contributing a hint today, flag off included.

If that was deliberate (some autocomplete values misclassifying identity fields), a one-line note here is enough and I'll drop it — though IGNORED_RAW_HINTS would scope the fix to the offending values rather than the whole attribute. If it was incidental to the heuristics work, restoring the entry is the safer call; there's no unit coverage on this file to catch the difference.

Restore the entry →

No code changes made — say the word if you'd like me to push either fix.
· branch PM-41294/identity-autofill-multi-partition-pipeline

"label",
"type",
"hint",
"autofill",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

QUESTION: Why is "autocomplete" dropped from the supported HTML attribute hints here?

Details

hints() feeds every HtmlInfo heuristic, not just the identity ones — isUsernameField(), isPasswordField(), and the card patterns all read the same attribute values. Dropping autocomplete means the standard web attribute (autocomplete="email", "cc-number", "new-password", …) no longer contributes a hint on web forms, and unlike the rest of this PR the change is not gated by FlagKey.IdentityAutofill, so it also affects Login and Card detection today.

The term was added by the merged identity-heuristics change (d23d1d1) and is removed again here with no mention in the PR description. If this is a deliberate revert because some autocomplete values were misclassifying fields, it would help to say so — and possibly to handle it via IGNORED_RAW_HINTS rather than dropping the whole attribute, since this file has no unit coverage to catch the regression.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@claude autocomplete is not being used by autofill framework so I'm removing it

@claude claude Bot Sep 9, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Claude finished @aj-rosado's task in 1m 6s —— View job


Question resolved — reasoning checks out

  • Gather context on HtmlInfoExtensions.hints() and attribute matching
  • Verify the claim that autocomplete never contributes a hint
  • Check for test coverage that would regress

Thanks — that explanation holds up, and I traced why.

hints() filters HtmlInfo.attributes by substring, case-insensitive match on the attribute name (HtmlInfoExtensions.kt:189-193StringExtensions.kt:9-18). So "autocomplete" in the list only ever mattered if a browser literally forwarded an attribute whose name contains autocomplete. Chromium's AutofillProvider doesn't: it surfaces the parsed autocomplete signal as ua-autofill-hints (alongside the server/crowdsourcing prediction hint attributes), not as the raw autocomplete attribute. Those are already admitted by the "autofill" and "hint" terms that remain — which is exactly what the KDoc on SUPPORTED_HTML_ATTRIBUTE_HINTS (HtmlInfoExtensions.kt:261-266) documents. And type / name / label still cover type="email", type="tel", name="cc-number", etc.

So the entry was dead weight rather than an active signal, and dropping it is the right call over routing it through IGNORED_RAW_HINTS (that filters values, not attribute names — it wouldn't have applied here anyway).

Two small notes, neither blocking:

  • The only theoretical loss is a non-Chromium WebView that forwards a raw autocomplete attribute; there autocomplete="new-password" / "cc-number" would no longer contribute. Given the framework path goes through AutofillProvider, that's not a case we see in practice.
  • No test regression to worry about: this file is untestable under unit tests (HtmlInfo wraps android.util.Pair, per the KDoc at HtmlInfoExtensions.kt:170-178), and the only autocomplete references in tests are FillAssistManagerTest selector-parsing cases, which are unrelated. Nothing to update.

Since the intent was that it's unused rather than a behavior revert, it may be worth a one-liner in the PR description so the flag-independent line doesn't read as an accidental drop to the next reviewer. No code change needed from my side — closing this out as answered.
• Branch: PM-41294/identity-autofill-multi-partition-pipeline

@aj-rosado aj-rosado added t:feature Change Type - Feature Development and removed t:feature Change Type - Feature Development labels Sep 9, 2026
@aj-rosado
aj-rosado changed the base branch from main to PM-41293/identity-autofill-fill-assist-field-mapping September 9, 2026 11:58
@aj-rosado
aj-rosado marked this pull request as ready for review September 10, 2026 14:26
@aj-rosado
aj-rosado requested review from a team and david-livefront as code owners September 10, 2026 14:26
private suspend fun buildFilledPartitions(
autofillRequest: AutofillRequest.Fillable,
getCipherInlinePresentationOrNull: () -> InlinePresentationSpec?,
): List<FilledPartition> = when (autofillRequest.partition) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

👍

/**
* Get the autofill value for the given [autofillView], or null if no value is available.
*/
@Suppress("CyclomaticComplexMethod")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Is this needed?

val autofillViews = traversalDataList
.selectCandidateAutofillViews(urlBarWebsite = urlBarWebsite) {
it !is AutofillView.Unused && it !is AutofillView.Identity
it !is AutofillView.Unused

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

👍


// Identity categories were Login categories before identity autofill, so with the flag
// off they must stay Login's to keep fill-assist coverage unchanged on those hosts.
val loginCategories = if (isIdentityAutofillEnabled) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Maybe rename this to fillAssistCategories since it can now contain identity categories too

is AutofillView.Identity.PostalAddressFull -> {
this.copy(data = this.data.copy(website = site))
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think this is supposed to be here, right?

this.isAddressCountryField -> AutofillHint.Identity.ADDRESS_COUNTRY
this.isPostalCodeField -> AutofillHint.Identity.POSTAL_CODE
this.isPhoneField -> AutofillHint.Identity.PHONE_FULL
this.isPhoneField -> AutofillHint.Login.USERNAME

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Does this mean that AutofillHint.Identity.PHONE_FULL is never used?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It will autofill phone numbers, just not display the identity when pressing on a phone number

nameView,
AutofillView.Identity.Email(data = emailLoginView.data),
),
(partition as AutofillPartition.Identity).views,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can we just compare the entire result

// container-redirect leftover, not an intentional sibling.
is AutofillView.Identity -> {
(id !in idsClaimedByAncestor)
.also { keep -> if (keep) claimedAutofillIds.add(id) }

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Could we just do this:

if (id !in idsClaimedByAncestor) {
    claimedAutofillIds.add(id)
}

That may be more straight forward to read.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-review Request a Claude code review app:password-manager Bitwarden Password Manager app context t:feature Change Type - Feature Development

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants