From 21365fc097233bfebc694681e23e2648d2d444a4 Mon Sep 17 00:00:00 2001 From: Miles Blackwood Date: Fri, 19 Sep 2025 16:33:08 -0400 Subject: [PATCH] Qualification debug. --- .../autofill-overlay-content.service.ts | 154 +++++++++++++++--- 1 file changed, 134 insertions(+), 20 deletions(-) diff --git a/apps/browser/src/autofill/services/autofill-overlay-content.service.ts b/apps/browser/src/autofill/services/autofill-overlay-content.service.ts index 07c97a5a344..bc638c975b1 100644 --- a/apps/browser/src/autofill/services/autofill-overlay-content.service.ts +++ b/apps/browser/src/autofill/services/autofill-overlay-content.service.ts @@ -56,6 +56,41 @@ import { DomQueryService } from "./abstractions/dom-query.service"; import { InlineMenuFieldQualificationService } from "./abstractions/inline-menu-field-qualifications.service"; import { AutoFillConstants } from "./autofill-constants"; +export type QualificationCriteria = { + formFieldElement: ElementWithOpId; + autofillFieldData: AutofillField; + pageDetails: AutofillPageDetails; +}; + +type Qualifier = (criteria?: QualificationCriteria) => boolean; +type QualificationMeta = { + qualifier: Qualifier; + alias: string; +}; +type QualificationMessageOptions = { + message: { + success: string; + failure: string; + }; +}; +type QualificationConfig = { + blocking?: false; // assume qualifiers are blocking unless otherwise indicated + effect?: (criteria?: QualificationCriteria) => void; +} & QualificationMessageOptions; + +type QualificationDefinition = QualificationMeta & QualificationConfig; +type QualificationResponse = QualificationMeta & { result: boolean; message: string }; +// type QualificationResolver = ( +// qualifier: Qualifier, +// criteria?: QualificationCriteria, +// ) => QualificationResponse; + +// type QualificationResponse = { +// lastQualified: Qualification; +// passed: Qualification[]; +// failed: Qualification[]; +// }; + export class AutofillOverlayContentService implements AutofillOverlayContentServiceInterface { pageDetailsUpdateRequired = false; private showInlineMenuIdentities: boolean; @@ -199,21 +234,111 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ autofillFieldData: AutofillField, pageDetails: AutofillPageDetails, ) { - if ( - currentlyInSandboxedIframe() || - this.formFieldElements.has(formFieldElement) || - this.isIgnoredField(autofillFieldData, pageDetails) - ) { - return; - } - - if (this.isHiddenField(formFieldElement, autofillFieldData)) { + const qualification = this.isQualifiedField({ + formFieldElement, + autofillFieldData, + pageDetails, + }); + if (!qualification) { return; } await this.setupOverlayListenersOnQualifiedField(formFieldElement, autofillFieldData); } + private qualifiers: QualificationDefinition[] = [ + { + qualifier: () => !currentlyInSandboxedIframe(), + alias: "notCurrentlyInSandboxedIframe", + message: { + success: "field not in sandboxed iframe", + failure: "field in sandboxed iframe", + }, + }, + { + qualifier: ({ formFieldElement }: QualificationCriteria) => + !this.formFieldElements.has(formFieldElement), + alias: "notPreviouslyQualified", + message: { + success: "field not previously qualified", + failure: "field previously qualified", + }, + }, + // Function should be entirely deconstructed first. + // { + // qualifier: ({ autofillFieldData, pageDetails }: QualificationCriteria) => + // !this.isIgnoredField(autofillFieldData, pageDetails), + // alias: "isNotIgnoredField", + // message: { + // success: "field is not ignored", + // failure: "field is ignored", + // }, + // }, + { + qualifier: ({ autofillFieldData }: QualificationCriteria) => + !this.ignoredFieldTypes.has(autofillFieldData.type), + alias: "fieldNotIgnoredType", + message: { + success: "field is not in ignored types", + failure: "field is in ignored types", + }, + }, + { + qualifier: ({ autofillFieldData, pageDetails }: QualificationCriteria) => + this.inlineMenuFieldQualificationService.isFieldForLoginForm( + autofillFieldData, + pageDetails, + ), + alias: "fieldIsForLoginForm", + message: { + success: "field is for login form", + failure: "field is not for login form", + }, + blocking: false, + effect: ({ autofillFieldData }: QualificationCriteria) => + this.setQualifiedLoginFillType(autofillFieldData), + }, + { + qualifier: ({ formFieldElement, autofillFieldData }: QualificationCriteria) => + !this.isHiddenField(formFieldElement, autofillFieldData), + alias: "isNotHiddenField", + message: { + success: "field is not hidden", + failure: "field is hidden", + }, + }, + ]; + + qualify( + definition: QualificationDefinition, + criteria: QualificationCriteria, + ): QualificationResponse { + const { + qualifier, + alias, + message: { success, failure }, + } = definition; + const result = qualifier(criteria); + return { alias, result, message: result ? success : failure, qualifier }; + } + + isQualifiedField(criteria: QualificationCriteria) { + const responses: QualificationResponse[] = []; + for (const definition of this.qualifiers) { + const response = this.qualify(definition, criteria); + responses.push(response); + if (response.result === false && definition?.blocking !== false) { + console.log({ element: criteria.formFieldElement, responses }); + return false; + } + if (response.result === true && definition.effect) { + void definition.effect(criteria); + } + } + console.log({ element: criteria.formFieldElement, responses }); + return true; + } + /** * Removes focus from the most recently focused field element. */ @@ -1056,17 +1181,6 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ autofillFieldData: AutofillField, pageDetails: AutofillPageDetails, ): boolean { - if (this.ignoredFieldTypes.has(autofillFieldData.type)) { - return true; - } - - if ( - this.inlineMenuFieldQualificationService.isFieldForLoginForm(autofillFieldData, pageDetails) - ) { - void this.setQualifiedLoginFillType(autofillFieldData); - return false; - } - if ( this.showInlineMenuCards && this.inlineMenuFieldQualificationService.isFieldForCreditCardForm(