Skip to content

[PM-37255] feat: Integrate fill-assist targeting rules into autofill parser#7066

Open
aj-rosado wants to merge 30 commits into
mainfrom
PM-37256/apply-fill-assist-rules
Open

[PM-37255] feat: Integrate fill-assist targeting rules into autofill parser#7066
aj-rosado wants to merge 30 commits into
mainfrom
PM-37256/apply-fill-assist-rules

Conversation

@aj-rosado

Copy link
Copy Markdown
Contributor

🎟️ Tracking

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

📔 Objective

Wires the fill-assist targeting-rules pipeline end-to-end into the Android autofill framework.

When the FillAssistTargetingRules feature flag is enabled and rules are available for the current host, the autofill parser replaces heuristic field detection with site-specific CSS-selector-based matching. Unmatched nodes are excluded entirely — there is no heuristic fallback when rules are active.

What's included:

  • Network layerFillAssistApi, FillAssistService, FillAssistManifestJson, FillAssistFormsJson: fetches a versioned manifest and forms JSON from the fill-assist CDN endpoint.
  • Data layerFillAssistManagerImpl, FillAssistDiskSource: parses CSS selectors into FillAssistRules (tag, id, name, type, role constraints per field), caches rules on disk, syncs on server-config change with a 6-hour re-fetch throttle.
  • CSS selector parser — handles >>> Shadow DOM notation, space-separated CSS descendant selectors (split on whitespace outside […] attribute brackets to preserve attribute values that contain spaces), #id shorthand, and [attr='value'] / [attr="value"] attribute selectors.
  • IntegrationAutofillParserImpl looks up host rules for the focused view's URI and calls AssistStructure.buildFillAssistViews to replace the heuristic view list when rules match.
  • View-node matchingFillAssistViewNodeExtensions.traverseForFillAssist traverses the AssistStructure tree; HtmlInfoExtensions.matchesSelectorClause does the per-node attribute comparison (kept in HtmlInfoExtensions alongside hints() since HtmlInfo.attributes uses android.util.Pair and is untestable in unit tests).
  • TestsFillAssistManagerTest, FillAssistServiceTest, FillAssistViewNodeExtensionsTest (all previously commented-out tests now passing via mockkStatic(HtmlInfo::matchesSelectorClause)), and AutofillParserTests updated for the new constructor parameters.

@aj-rosado aj-rosado added the ai-review-vnext Request a Claude code review using the vNext workflow label Jun 16, 2026
@github-actions github-actions Bot added the app:password-manager Bitwarden Password Manager app context label Jun 16, 2026
@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

🤖 Bitwarden Claude Code Review

Overall Assessment: REQUEST CHANGES

Reviewed the fill-assist targeting-rules integration into the autofill parser: the toEffectiveViews/buildFillAssistViews pipeline, the matchesSelectorClause AND-matching logic, the CSS descendant-separator regex change, VaultSyncManager sync wiring, and the accompanying unit tests. The core parser logic (empty-match handling, partition coverage checks, no-heuristic-fallback semantics) is sound and well tested. However, the most recent commit (643be12, a revert) leaves the branch in a state that will not compile.

Code Review Details
  • ❌ : FillAssistManager referenced but not imported in VaultSyncManagerImpl — unresolved reference / compile break introduced by the 643be12 revert
    • app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/VaultSyncManagerImpl.kt:83
  • 🎨 : FillAssistManager import out of alphabetical order (same revert)
    • app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/di/VaultManagerModule.kt:47

@aj-rosado aj-rosado changed the title Add fill assist logic to Autofill [PM-37255] feat: Integrate fill-assist targeting rules into autofill parser Jun 17, 2026
@github-actions github-actions Bot added the t:feature Change Type - Feature Development label Jun 25, 2026
@aj-rosado aj-rosado marked this pull request as ready for review June 30, 2026 10:04
Base automatically changed from PM-37255/fill-assist-integration to main June 30, 2026 10:17
?: return AutofillRequest.Unfillable

val packageName =
traversalDataList.buildPackageNameOrNull(assistStructure = assistStructure)

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 clean this up a bit

val packageName = traversalDataList.buildPackageNameOrNull(
    assistStructure = assistStructure,
)

?: viewsLists
.flatten()
.filter { it !is AutofillView.Unused })
.map { it.updateWebsiteIfNecessary(website = urlBarWebsite) }

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 like that this was moved out into it's own method.

What do you think of this minor update:

private fun List<ViewNodeTraversalData>.toAutofillViews(
    urlBarWebsite: String?,
): List<AutofillView> {
    val viewsLists = map { it.autofillViews }
    val autofillViewLists = viewsLists
        .filter { views -> views.any { it.data.isFocused } }
        .flatten()
        .filter { it !is AutofillView.Unused }
        .takeUnless { it.isEmpty() }
        ?: viewsLists
            .flatten()
            .filter { it !is AutofillView.Unused }
    return autofillViewLists.map { it.updateWebsiteIfNecessary(website = urlBarWebsite) }
}

I have never found that original block of code particularly readable 😄

.map { it.updateWebsiteIfNecessary(website = urlBarWebsite) }

// Find the focused view, or fallback to the first fillable item on the screen (so
// we at least have something to hook into)

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 leave in the comments in place? Autofill is confusing enough as it is, I want to make sure we leave goo notes for the next person.

        // Find the focused view, or fallback to the first fillable item on the screen (so
        // we at least have something to hook into)
        val focusedView = autofillViews
            .firstOrNull { it.data.isFocused }
            ?: autofillViews.firstOrNull()
            // The view is unfillable if there are no focused views.
            ?: return AutofillRequest.Unfillable


val blockListedURIs = settingsRepository.blockedAutofillUris + BLOCK_LISTED_URIS
if (blockListedURIs.contains(uri)) {
// The view is unfillable if the URI is block listed.

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.

Same here, let's leave the comments in for posterity.


// Get inline information if available
val isInlineAutofillEnabled = settingsRepository.isInlineAutofillEnabled
Timber.d("Autofill request isInlineEnabled=$isInlineAutofillEnabled -- ${fillRequest?.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.

Why are we removing this?

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.

I have added a TON of debug logs. When removing probably removed this one with the extra ones by mistake. Will re-add

* when the feature flag is enabled and the host rules cover the current partition type;
* otherwise returns the heuristic [autofillViews].
*/
private fun resolveEffectiveViews(

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.

What do you think about making this an extension function?

    private fun List<AutofillView>.toEffectiveViews(
        assistStructure: AssistStructure,
        uri: String?,
        focusedView: AutofillView,
        urlBarWebsite: String?,
    ): List<AutofillView> {

?.takeUnless { it.startsWith("androidapp://") }
?.toUri()
?.host
?.takeIf { featureFlagManager.getFeatureFlag(FlagKey.FillAssistTargetingRules) }

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 return early instead of putting this check in the chain?

        if (!featureFlagManager.getFeatureFlag(FlagKey.FillAssistTargetingRules)) {
            return autofillViews
        }

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.

https://github.com/bitwarden/android/pull/7075/changes#diff-e1c5c53907c2162defc188d108bdeb90c4792990fbd3ea388b62e75731c2da6cL215

Next PR in chain will remove that. Are you ok if we keep it here for now to avoid more conflicts when merging?

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.

Why is it being removed in that PR?

val urlBarWebsite = traversalDataList
.flatMap { it.urlBarWebsites }
.firstOrNull()
val autofillViews = traversalDataList.toAutofillViews(urlBarWebsite = urlBarWebsite)

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.

The new buildFillAssistViews is awfully similar to the existing logic for parsing the assist structure. It's hard for me to tell if it is possible but can we optimize this to parse the structure only once?

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 is possible but as It adds complexity to the traverse, not sure if it a real improvement.
I have created a draft PR with a possible solution for this.
#7144

@codecov

codecov Bot commented Jul 6, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 72.72727% with 30 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.24%. Comparing base (2022507) to head (643be12).
⚠️ Report is 75 commits behind head on main.

Files with missing lines Patch % Lines
...bitwarden/data/autofill/util/HtmlInfoExtensions.kt 0.00% 13 Missing ⚠️
...twarden/data/autofill/parser/AutofillParserImpl.kt 82.69% 2 Missing and 7 partials ⚠️
...bitwarden/data/autofill/util/ViewNodeExtensions.kt 0.00% 8 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #7066      +/-   ##
==========================================
- Coverage   86.33%   85.24%   -1.10%     
==========================================
  Files         918      901      -17     
  Lines       65587    65186     -401     
  Branches     9305     9624     +319     
==========================================
- Hits        56626    55569    -1057     
- Misses       5755     6167     +412     
- Partials     3206     3450     +244     
Flag Coverage Δ
app-data 17.35% <72.72%> (+0.12%) ⬆️
app-ui-auth-tools 18.77% <0.00%> (-0.45%) ⬇️
app-ui-platform 16.45% <0.00%> (-0.29%) ⬇️
app-ui-vault 27.46% <0.00%> (-0.98%) ⬇️
authenticator 6.12% <0.00%> (-0.13%) ⬇️
lib-core-network-bridge 4.05% <0.00%> (-0.04%) ⬇️
lib-data-ui 1.14% <0.00%> (-0.02%) ⬇️

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.

?.host
?.takeIf { featureFlagManager.getFeatureFlag(FlagKey.FillAssistTargetingRules) }
?.let { host ->
fillAssistManager.getFillAssistRules()?.hostRules?.get(host.removePrefix("www."))

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.

🧹 DEBT: getFillAssistRules() is invoked on the autofill parse hot path (once per parse() — i.e. per field focus / fill request). Its implementation re-reads the SharedPreferences string and fully re-deserializes the entire rules blob every call:

// FillAssistDiskSourceImpl
override fun getFillAssistRules(serverUrl: String): FillAssistRules? =
    getString(FILL_ASSIST_RULES_KEY.appendIdentifier(serverUrl))
        ?.let { json.decodeFromStringOrNull(it) }

Since the cached blob can contain rules for many hosts, this JSON parse repeats on every autofill interaction with no in-memory memoization. Consider caching the deserialized FillAssistRules in memory (invalidated on sync) so the parse path avoids repeated full deserialization. Gated behind the feature flag, so not urgent — but worth addressing before broad rollout.

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 updated

@claude claude Bot Jul 6, 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 20s —— View job


Verifying the caching update

  • Reviewed the cache fill assist rules commit (1d3381f)
  • Confirmed the hot-path re-deserialization is resolved
  • Checked test coverage

Resolved. The DEBT concern is addressed. getFillAssistRules() now memoizes the deserialized FillAssistRules in the cachedRules field and short-circuits before touching disk (FillAssistManagerImpl.kt:146), so the full JSON blob is no longer re-parsed on every parse() call. Invalidation is wired correctly:

  • reset on server-config change (init block, :78)
  • reset after a sync stores new rules (:137)

Test coverage is solid — the three new cases cover the cache hit (disk read exactly = 1), re-read after server-config change, and returning the latest rules after a sync.

One carry-over note: this newly-added cachedRules var is written from three contexts (init/unconfined, sync/io, and getFillAssistRules on the binder thread) without @Volatile. That's the same publication concern already tracked in the sibling ♻️ DEBT thread on this field — marking @Volatile there would also cover this cache. No action needed here beyond that existing thread.

} answers {
mutableFillAssistFlagFlow.value
}
every { getFeatureFlagFlow(FlagKey.ManageDevices) } returns mutableFillAssistFlagFlow

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.

📝 SUGGESTED: This stub wires the fill-assist flow to FlagKey.ManageDevices, but AutofillParserImpl only ever calls getFeatureFlag(FlagKey.FillAssistTargetingRules) — never getFeatureFlagFlow. This looks like a copy-paste leftover; it's dead setup and misleadingly ties the test's flag flow to an unrelated flag. Recommend removing this line.

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 updated

@claude claude Bot Jul 6, 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 58s —— View job


Verified — FlagKey.ManageDevices copy-paste leftover resolved ✅

Confirmed the update. The stub now wires to the correct flag:

private val mutableFillAssistFlagFlow = MutableStateFlow(false)
private val featureFlagManager: FeatureFlagManager = mockk {
    every {
        getFeatureFlag(FlagKey.FillAssistTargetingRules)
    } answers {
        mutableFillAssistFlagFlow.value
    }
    every {
        getFeatureFlagFlow(FlagKey.FillAssistTargetingRules)
    } returns mutableFillAssistFlagFlow
}

The misleading tie to the unrelated FlagKey.ManageDevices is gone, and the MutableStateFlow now cleanly backs the flag value that the tests toggle (mutableFillAssistFlagFlow.value = true at lines 1004/1064/1124/1197). Original concern resolved.

One residual nit (non-blocking): AutofillParserImpl only ever calls getFeatureFlag(...) — never getFeatureFlagFlow(...) (confirmed at AutofillParserImpl.kt:220). So the getFeatureFlagFlow stub (lines 94–95) is still unexercised setup and could be dropped. Purely cosmetic — no action required.

import com.bitwarden.vault.FolderView
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
import com.x8bit.bitwarden.data.auth.manager.UserLogoutManager
import com.x8bit.bitwarden.data.autofill.manager.FillAssistManager

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.

📝 SUGGESTED: Import out of order — autofill.manager.FillAssistManager is placed between the two auth.manager imports (auth sorts before autofill). Run IDE optimize-imports so it lands after UserStateManager. Same misplacement exists in VaultManagerModule.kt. Minor (detekt ImportOrdering is disabled, so CI won't catch it) but deviates from the codebase's sorted-import convention.

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 updated

@claude claude Bot Jul 6, 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 2m 17s —— View job


Import fix review

  • Read VaultSyncManagerImpl.kt imports
  • Read VaultManagerModule.kt imports
  • Assess whether the update was correct
  • Fix the regression and push

⚠️ The fix in f677f80 (Fixing fillAssistManager import) removed the FillAssistManager import entirely from VaultSyncManagerImpl.kt instead of reordering it. But FillAssistManager is still referenced at VaultSyncManagerImpl.kt:83 (constructor param) and :346 (fillAssistManager.syncIfNecessary()), and it lives in a different package (com.x8bit.bitwarden.data.autofill.manager) — so this would have broken compilation.

I pushed a fix (2219e6f) that:

  1. VaultSyncManagerImpl.kt — re-added the import in its correctly sorted position (after auth.repository.util.userSwitchingChangesFlow, before platform.datasource.disk.SettingsDiskSource, since autofill sorts after auth and before platform).
  2. VaultManagerModule.kt — this file still had the import misplaced between the vault.manager.* imports (line 47). Moved it up to after auth.manager.UserStateManager, matching the sorted convention.

Both imports now land where IDE optimize-imports would put them.

💡 Recommend re-running a local build (./gradlew app:assembleStandardDebug or the compile task) before merging to confirm — the removed import would have surfaced as a hard compile error, so it's worth a green build check.

Comment on lines +61 to +72
private val LOGIN_FILL_ASSIST_CATEGORIES: List<String> = listOf(
"account-login",
"account-creation",
"account-update",
)

/**
* A list of categories from Fill Assist that are used for [AutofillView.Card]
*/
private val CARD_FILL_ASSIST_CATEGORIES: List<String> = listOf(
"payment-card",
)

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: These hardcoded category allowlists are the gate that decides whether fill-assist replaces heuristics for a given partition. Any login-related category the CDN emits that isn't listed here will silently fall back to heuristics. Notably, FillAssistViewNodeExtensionsTest uses the category "account-registration" (with a newPassword field), which is not in LOGIN_FILL_ASSIST_CATEGORIES — so a real registration form served with that category would never trigger fill-assist even though the field mapping supports it.

Is the complete set of server categories confirmed to be exactly account-login/account-creation/account-update (+ payment-card)? If new categories can appear, consider deriving the partition mapping from the field keys present in the rule rather than a fixed category allowlist, to avoid silent gaps as the schema evolves.

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.

If a new category appears we will need to manually add it. As this falls back to heuristics is not a problem.

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 answered

@claude claude Bot Jul 6, 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 42s —— View job


Verified — hardcoded category allowlist behavior confirmed ✅

Confirmed the author's answer. The concern raised was that any login-related category the CDN emits that isn't in LOGIN_FILL_ASSIST_CATEGORIES / CARD_FILL_ASSIST_CATEGORIES would silently fall back to heuristics.

Re-reading the gating logic at AutofillParserImpl.kt:226-241, the answer holds:

  • When coversCurrentPartition is false (i.e. an unlisted category), toEffectiveViews returns this — the heuristic autofill views. That's the existing, working production path, so an unrecognized category degrades gracefully rather than breaking autofill. Not a correctness issue.
  • Adding a new category is a one-line change to the relevant allowlist, which is easy to catch and extend when the schema evolves.

On the specific "account-registration" note from the original comment: I checked FillAssistViewNodeExtensionsTest.kt:114 — that category is only exercised against buildFillAssistViews / traverseForFillAssist (the field-mapping/traversal layer), not the toEffectiveViews category gate. So there's no test asserting fill-assist coverage for a category that the gate would reject; the two layers are tested independently and there's no misleading assertion. My original example conflated the two layers.

Resolving as answered — the fall-back-to-heuristics design is a deliberate and safe choice. 👍

@aj-rosado aj-rosado requested a review from david-livefront July 6, 2026 14:13

// In-memory cache to avoid re-deserializing the full rules blob on every autofill parse.
// Populated lazily on first read; updated in-place when a sync stores new rules.
private var cachedRules: FillAssistRules? = null

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.

♻️ DEBT: cachedRules is a plain var written from three concurrency contexts without a memory barrier.

Details and fix

cachedRules is written from:

  • the init block on unconfinedScope (cachedRules = null on server-config change),
  • sync() on ioScope (cachedRules = null after storing new rules),
  • getFillAssistRules() (?.also { cachedRules = it }), which runs on the binder thread that the autofill framework invokes parse() on.

Because the field is neither @Volatile nor guarded by a lock, a cachedRules = null reset performed on one thread may not be visible to the parser thread, so a parse can serve stale rules after a server-config change until the JVM happens to publish the write.

The impact is benign (transient stale rules or an extra disk read, no crash), but a cheap fix restores the intended invalidation semantics:

@Volatile
private var cachedRules: FillAssistRules? = null

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 reverted this change, we are getting the data from sharedPreferences and not disk, this cache is not really necessary

@claude claude Bot Jul 6, 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 56s —— View job


Agreed — reverting the cache is the right call ✅

  • Re-read FillAssistManagerImpl.getFillAssistRules() (post-revert)
  • Traced the read path into FillAssistDiskSourceImpl
  • Re-evaluated the original DEBT concern

You're right, and thanks for pushing back — my original DEBT note conflated two different costs.

On disk I/O: getFillAssistRules()FillAssistDiskSourceImpl.getFillAssistRules()getString(...) (FillAssistDiskSourceImpl.kt:35). SharedPreferences loads its backing file into an in-memory map once and serves getString from there — so there's no per-call disk read. My phrasing "re-reads the SharedPreferences string" implied I/O that isn't happening. You're correct on that.

On the residual cost: the only thing that still repeats per call is the json.decodeFromStringOrNull(...) deserialization. But parse() fires per field-focus / fill request — a user-initiated interaction, not a tight loop — so a single JSON parse per autofill interaction is negligible in practice. The in-memory cache was buying a micro-optimization on a low-frequency path.

Net: reverting is the better trade-off. It removes the added complexity and eliminates the @Volatile/publication concern from the sibling ♻️ DEBT thread on cachedRules (that field no longer exists, so that thread is moot too). No action needed here.

If deserialization ever does show up on a profile once this is broadly rolled out, an in-memory cache can be reintroduced then — but it's not warranted now. Resolving as answered. 👍

aj-rosado and others added 4 commits July 6, 2026 16:37
The previous fix removed the FillAssistManager import from
VaultSyncManagerImpl.kt entirely, breaking compilation since the class
is still referenced. Re-add it in its correctly sorted position, and
also correct the misplaced import in VaultManagerModule.kt.

Co-authored-by: aj-rosado <aj-rosado@users.noreply.github.com>
val HtmlInfo?.isInputField: Boolean get() = this?.tag == "input"

/**
* Whether this [HtmlInfo] matches the given [SelectorClause].

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.

The link here is broken, can we make it [FillAssistRules.SelectorClause]

clause.name == null &&
clause.type == null &&
clause.role == null
fun hasAttr(key: String, value: String) = attrs.any { it.first == key && it.second == value }

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.

The formatter really wants a newline before the inline function

clause.type == null &&
clause.role == null
fun hasAttr(key: String, value: String) = attrs.any { it.first == key && it.second == value }
return listOf(

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.

This can be mapOf

val data = toAutofillViewData(autofillId = id, website = website)
matchingEntries.firstNotNullOfOrNull { (key, _) ->
key.toAutofillViewForFieldKey(
data,

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.

This should either have a name or be 1 line.

    key.toAutofillViewForFieldKey(data)

OR

    key.toAutofillViewForFieldKey(
        data = data,
    )

"cardholderName" -> AutofillView.Card.CardholderName(data = data)
"cardExpirationDate" -> AutofillView.Card.ExpirationDate(data = data)
"cardExpirationMonth" -> AutofillView.Card.ExpirationMonth(data = data, monthValue = null)
"cardExpirationYear" -> AutofillView.Card.ExpirationYear(data = data, yearValue = null)

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 make some consts for these magic strings?

clause.id to "id",
clause.name to "name",
clause.type to "type",
clause.role to "role",

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 make these consts?

private val authDiskSource: AuthDiskSource,
private val vaultDiskSource: VaultDiskSource,
private val vaultSdkSource: VaultSdkSource,
private val fillAssistManager: FillAssistManager,

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.

CRITICAL: FillAssistManager is referenced but not imported — this will not compile.

Details and fix

The most recent commit (643be12 "Revert 'Restore FillAssistManager import and fix import ordering'") removed the import com.x8bit.bitwarden.data.autofill.manager.FillAssistManager line from this file, but the constructor parameter here (line 83) and the fillAssistManager.syncIfNecessary() call (line 343) still reference the type.

FillAssistManager lives in com.x8bit.bitwarden.data.autofill.manager, a different package from this file's com.x8bit.bitwarden.data.vault.manager, so it cannot resolve without an explicit import — this is an unresolved reference / compile error.

Restore the import (in alphabetical position, after ...auth.repository.util.userSwitchingChangesFlow):

import com.x8bit.bitwarden.data.autofill.manager.FillAssistManager

import com.x8bit.bitwarden.data.vault.manager.VaultLockManagerImpl
import com.x8bit.bitwarden.data.vault.manager.VaultMigrationManager
import com.x8bit.bitwarden.data.vault.manager.VaultMigrationManagerImpl
import com.x8bit.bitwarden.data.autofill.manager.FillAssistManager

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.

🎨 SUGGESTED: Import is out of alphabetical order — the same 643be12 revert moved it here, between the vault.manager.* imports.

Details

com.x8bit.bitwarden.data.autofill.manager.FillAssistManager belongs with the other data.auth/data.autofill imports near the top of the block (before data.platform.*), not between VaultMigrationManagerImpl and VaultSyncManager. This compiles fine (detekt ImportOrdering is disabled), but it's inconsistent with the rest of the file's ordering.

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

Labels

ai-review-vnext Request a Claude code review using the vNext workflow 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