1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-16 16:23:44 +00:00

[PM-8027] Reworking inline menu to qualify and setup the listeners for each form field after page deatils have been collected

This commit is contained in:
Cesar Gonzalez
2024-05-31 14:58:21 -05:00
parent 43246056a3
commit f3d41f91aa
7 changed files with 102 additions and 47 deletions

View File

@@ -1,6 +1,5 @@
import AutofillPageDetails from "../models/autofill-page-details"; import AutofillPageDetails from "../models/autofill-page-details";
import { AutofillOverlayContentService } from "../services/abstractions/autofill-overlay-content.service"; import { AutofillOverlayContentService } from "../services/abstractions/autofill-overlay-content.service";
import { AutofillFieldQualificationService } from "../services/autofill-field-qualification.service";
import CollectAutofillContentService from "../services/collect-autofill-content.service"; import CollectAutofillContentService from "../services/collect-autofill-content.service";
import DomElementVisibilityService from "../services/dom-element-visibility.service"; import DomElementVisibilityService from "../services/dom-element-visibility.service";
import InsertAutofillContentService from "../services/insert-autofill-content.service"; import InsertAutofillContentService from "../services/insert-autofill-content.service";
@@ -13,7 +12,6 @@ import {
} from "./abstractions/autofill-init"; } from "./abstractions/autofill-init";
class AutofillInit implements AutofillInitInterface { class AutofillInit implements AutofillInitInterface {
private readonly autofillFieldQualificationService: AutofillFieldQualificationService;
private readonly autofillOverlayContentService: AutofillOverlayContentService | undefined; private readonly autofillOverlayContentService: AutofillOverlayContentService | undefined;
private readonly domElementVisibilityService: DomElementVisibilityService; private readonly domElementVisibilityService: DomElementVisibilityService;
private readonly collectAutofillContentService: CollectAutofillContentService; private readonly collectAutofillContentService: CollectAutofillContentService;
@@ -40,7 +38,6 @@ class AutofillInit implements AutofillInitInterface {
* @param autofillOverlayContentService - The autofill overlay content service, potentially undefined. * @param autofillOverlayContentService - The autofill overlay content service, potentially undefined.
*/ */
constructor(autofillOverlayContentService?: AutofillOverlayContentService) { constructor(autofillOverlayContentService?: AutofillOverlayContentService) {
this.autofillFieldQualificationService = new AutofillFieldQualificationService();
this.autofillOverlayContentService = autofillOverlayContentService; this.autofillOverlayContentService = autofillOverlayContentService;
this.domElementVisibilityService = new DomElementVisibilityService(); this.domElementVisibilityService = new DomElementVisibilityService();
this.collectAutofillContentService = new CollectAutofillContentService( this.collectAutofillContentService = new CollectAutofillContentService(
@@ -102,16 +99,6 @@ class AutofillInit implements AutofillInitInterface {
const pageDetails: AutofillPageDetails = const pageDetails: AutofillPageDetails =
await this.collectAutofillContentService.getPageDetails(); await this.collectAutofillContentService.getPageDetails();
// console.log(pageDetails);
// pageDetails.fields.forEach((field) => {
// const isLoginField = this.autofillFieldQualificationService.isFieldForLoginForm(
// field,
// pageDetails,
// );
//
// console.log(isLoginField, field);
// });
if (sendDetailsInResponse) { if (sendDetailsInResponse) {
return pageDetails; return pageDetails;
} }

View File

@@ -1,6 +1,7 @@
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import AutofillField from "../../models/autofill-field"; import AutofillField from "../../models/autofill-field";
import AutofillPageDetails from "../../models/autofill-page-details";
import { ElementWithOpId, FormFieldElement } from "../../types"; import { ElementWithOpId, FormFieldElement } from "../../types";
type OpenAutofillOverlayOptions = { type OpenAutofillOverlayOptions = {
@@ -19,6 +20,7 @@ interface AutofillOverlayContentService {
setupAutofillOverlayListenerOnField( setupAutofillOverlayListenerOnField(
autofillFieldElement: ElementWithOpId<FormFieldElement>, autofillFieldElement: ElementWithOpId<FormFieldElement>,
autofillFieldData: AutofillField, autofillFieldData: AutofillField,
pageDetails: AutofillPageDetails,
): Promise<void>; ): Promise<void>;
openAutofillOverlay(options: OpenAutofillOverlayOptions): void; openAutofillOverlay(options: OpenAutofillOverlayOptions): void;
removeAutofillOverlay(): void; removeAutofillOverlay(): void;

View File

@@ -4,6 +4,7 @@ import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authenticatio
import { EVENTS, AutofillOverlayVisibility } from "@bitwarden/common/autofill/constants"; import { EVENTS, AutofillOverlayVisibility } from "@bitwarden/common/autofill/constants";
import AutofillField from "../models/autofill-field"; import AutofillField from "../models/autofill-field";
import AutofillPageDetails from "../models/autofill-page-details";
import { createAutofillFieldMock } from "../spec/autofill-mocks"; import { createAutofillFieldMock } from "../spec/autofill-mocks";
import { flushPromises } from "../spec/testing-utils"; import { flushPromises } from "../spec/testing-utils";
import { ElementWithOpId, FormFieldElement } from "../types"; import { ElementWithOpId, FormFieldElement } from "../types";
@@ -27,6 +28,8 @@ function createMutationRecordMock(customFields = {}): MutationRecord {
}; };
} }
const temporaryPageDetailsMock = mock<AutofillPageDetails>();
const defaultWindowReadyState = document.readyState; const defaultWindowReadyState = document.readyState;
const defaultDocumentVisibilityState = document.visibilityState; const defaultDocumentVisibilityState = document.visibilityState;
describe("AutofillOverlayContentService", () => { describe("AutofillOverlayContentService", () => {
@@ -179,6 +182,7 @@ describe("AutofillOverlayContentService", () => {
await autofillOverlayContentService.setupAutofillOverlayListenerOnField( await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
autofillFieldElement, autofillFieldElement,
autofillFieldData, autofillFieldData,
temporaryPageDetailsMock,
); );
expect(autofillFieldElement.addEventListener).not.toHaveBeenCalled(); expect(autofillFieldElement.addEventListener).not.toHaveBeenCalled();
@@ -190,6 +194,7 @@ describe("AutofillOverlayContentService", () => {
await autofillOverlayContentService.setupAutofillOverlayListenerOnField( await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
autofillFieldElement, autofillFieldElement,
autofillFieldData, autofillFieldData,
temporaryPageDetailsMock,
); );
expect(autofillFieldElement.addEventListener).not.toHaveBeenCalled(); expect(autofillFieldElement.addEventListener).not.toHaveBeenCalled();
@@ -201,6 +206,7 @@ describe("AutofillOverlayContentService", () => {
await autofillOverlayContentService.setupAutofillOverlayListenerOnField( await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
autofillFieldElement, autofillFieldElement,
autofillFieldData, autofillFieldData,
temporaryPageDetailsMock,
); );
expect(autofillFieldElement.addEventListener).not.toHaveBeenCalled(); expect(autofillFieldElement.addEventListener).not.toHaveBeenCalled();
@@ -213,6 +219,7 @@ describe("AutofillOverlayContentService", () => {
await autofillOverlayContentService.setupAutofillOverlayListenerOnField( await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
autofillFieldElement, autofillFieldElement,
autofillFieldData, autofillFieldData,
temporaryPageDetailsMock,
); );
expect(autofillFieldElement.addEventListener).not.toHaveBeenCalled(); expect(autofillFieldElement.addEventListener).not.toHaveBeenCalled();
@@ -225,6 +232,7 @@ describe("AutofillOverlayContentService", () => {
await autofillOverlayContentService.setupAutofillOverlayListenerOnField( await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
autofillFieldElement, autofillFieldElement,
autofillFieldData, autofillFieldData,
temporaryPageDetailsMock,
); );
expect(autofillFieldElement.addEventListener).not.toHaveBeenCalled(); expect(autofillFieldElement.addEventListener).not.toHaveBeenCalled();
@@ -236,6 +244,7 @@ describe("AutofillOverlayContentService", () => {
await autofillOverlayContentService.setupAutofillOverlayListenerOnField( await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
autofillFieldElement, autofillFieldElement,
autofillFieldData, autofillFieldData,
temporaryPageDetailsMock,
); );
expect(autofillFieldElement.addEventListener).not.toHaveBeenCalled(); expect(autofillFieldElement.addEventListener).not.toHaveBeenCalled();
@@ -247,6 +256,7 @@ describe("AutofillOverlayContentService", () => {
await autofillOverlayContentService.setupAutofillOverlayListenerOnField( await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
autofillFieldElement, autofillFieldElement,
autofillFieldData, autofillFieldData,
temporaryPageDetailsMock,
); );
expect(autofillFieldElement.addEventListener).not.toHaveBeenCalled(); expect(autofillFieldElement.addEventListener).not.toHaveBeenCalled();
@@ -259,6 +269,7 @@ describe("AutofillOverlayContentService", () => {
await autofillOverlayContentService.setupAutofillOverlayListenerOnField( await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
autofillFieldElement, autofillFieldElement,
autofillFieldData, autofillFieldData,
temporaryPageDetailsMock,
); );
expect(autofillFieldElement.addEventListener).not.toHaveBeenCalled(); expect(autofillFieldElement.addEventListener).not.toHaveBeenCalled();
@@ -272,6 +283,7 @@ describe("AutofillOverlayContentService", () => {
await autofillOverlayContentService.setupAutofillOverlayListenerOnField( await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
autofillFieldElement, autofillFieldElement,
autofillFieldData, autofillFieldData,
temporaryPageDetailsMock,
); );
expect(sendExtensionMessageSpy).toHaveBeenCalledWith("getAutofillOverlayVisibility"); expect(sendExtensionMessageSpy).toHaveBeenCalledWith("getAutofillOverlayVisibility");
@@ -287,6 +299,7 @@ describe("AutofillOverlayContentService", () => {
await autofillOverlayContentService.setupAutofillOverlayListenerOnField( await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
autofillFieldElement, autofillFieldElement,
autofillFieldData, autofillFieldData,
temporaryPageDetailsMock,
); );
expect(autofillOverlayContentService["autofillOverlayVisibility"]).toEqual( expect(autofillOverlayContentService["autofillOverlayVisibility"]).toEqual(
@@ -310,6 +323,7 @@ describe("AutofillOverlayContentService", () => {
await autofillOverlayContentService.setupAutofillOverlayListenerOnField( await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
autofillFieldElement, autofillFieldElement,
autofillFieldData, autofillFieldData,
temporaryPageDetailsMock,
); );
expect(autofillFieldElement.removeEventListener).toHaveBeenNthCalledWith( expect(autofillFieldElement.removeEventListener).toHaveBeenNthCalledWith(
@@ -334,6 +348,7 @@ describe("AutofillOverlayContentService", () => {
await autofillOverlayContentService.setupAutofillOverlayListenerOnField( await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
autofillFieldElement, autofillFieldElement,
autofillFieldData, autofillFieldData,
temporaryPageDetailsMock,
); );
}); });
@@ -357,6 +372,7 @@ describe("AutofillOverlayContentService", () => {
await autofillOverlayContentService.setupAutofillOverlayListenerOnField( await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
autofillFieldElement, autofillFieldElement,
autofillFieldData, autofillFieldData,
temporaryPageDetailsMock,
); );
jest.spyOn(globalThis.customElements, "define").mockImplementation(); jest.spyOn(globalThis.customElements, "define").mockImplementation();
}); });
@@ -440,6 +456,7 @@ describe("AutofillOverlayContentService", () => {
await autofillOverlayContentService.setupAutofillOverlayListenerOnField( await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
spanAutofillFieldElement, spanAutofillFieldElement,
autofillFieldData, autofillFieldData,
temporaryPageDetailsMock,
); );
spanAutofillFieldElement.dispatchEvent(new Event("input")); spanAutofillFieldElement.dispatchEvent(new Event("input"));
@@ -451,6 +468,7 @@ describe("AutofillOverlayContentService", () => {
await autofillOverlayContentService.setupAutofillOverlayListenerOnField( await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
autofillFieldElement, autofillFieldElement,
autofillFieldData, autofillFieldData,
temporaryPageDetailsMock,
); );
autofillFieldElement.dispatchEvent(new Event("input")); autofillFieldElement.dispatchEvent(new Event("input"));
@@ -467,6 +485,7 @@ describe("AutofillOverlayContentService", () => {
await autofillOverlayContentService.setupAutofillOverlayListenerOnField( await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
passwordFieldElement, passwordFieldElement,
autofillFieldData, autofillFieldData,
temporaryPageDetailsMock,
); );
passwordFieldElement.dispatchEvent(new Event("input")); passwordFieldElement.dispatchEvent(new Event("input"));
@@ -486,6 +505,7 @@ describe("AutofillOverlayContentService", () => {
await autofillOverlayContentService.setupAutofillOverlayListenerOnField( await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
autofillFieldElement, autofillFieldElement,
autofillFieldData, autofillFieldData,
temporaryPageDetailsMock,
); );
autofillFieldElement.dispatchEvent(new Event("input")); autofillFieldElement.dispatchEvent(new Event("input"));
@@ -504,6 +524,7 @@ describe("AutofillOverlayContentService", () => {
await autofillOverlayContentService.setupAutofillOverlayListenerOnField( await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
autofillFieldElement, autofillFieldElement,
autofillFieldData, autofillFieldData,
temporaryPageDetailsMock,
); );
autofillFieldElement.dispatchEvent(new Event("input")); autofillFieldElement.dispatchEvent(new Event("input"));
@@ -517,6 +538,7 @@ describe("AutofillOverlayContentService", () => {
await autofillOverlayContentService.setupAutofillOverlayListenerOnField( await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
autofillFieldElement, autofillFieldElement,
autofillFieldData, autofillFieldData,
temporaryPageDetailsMock,
); );
autofillFieldElement.dispatchEvent(new Event("input")); autofillFieldElement.dispatchEvent(new Event("input"));
@@ -531,6 +553,7 @@ describe("AutofillOverlayContentService", () => {
await autofillOverlayContentService.setupAutofillOverlayListenerOnField( await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
autofillFieldElement, autofillFieldElement,
autofillFieldData, autofillFieldData,
temporaryPageDetailsMock,
); );
autofillFieldElement.dispatchEvent(new Event("input")); autofillFieldElement.dispatchEvent(new Event("input"));
@@ -546,6 +569,7 @@ describe("AutofillOverlayContentService", () => {
await autofillOverlayContentService.setupAutofillOverlayListenerOnField( await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
autofillFieldElement, autofillFieldElement,
autofillFieldData, autofillFieldData,
temporaryPageDetailsMock,
); );
autofillFieldElement.dispatchEvent(new Event("input")); autofillFieldElement.dispatchEvent(new Event("input"));
@@ -563,6 +587,7 @@ describe("AutofillOverlayContentService", () => {
await autofillOverlayContentService.setupAutofillOverlayListenerOnField( await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
autofillFieldElement, autofillFieldElement,
autofillFieldData, autofillFieldData,
temporaryPageDetailsMock,
); );
}); });
@@ -613,6 +638,7 @@ describe("AutofillOverlayContentService", () => {
await autofillOverlayContentService.setupAutofillOverlayListenerOnField( await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
autofillFieldElement, autofillFieldElement,
autofillFieldData, autofillFieldData,
temporaryPageDetailsMock,
); );
autofillFieldElement.dispatchEvent(new Event("focus")); autofillFieldElement.dispatchEvent(new Event("focus"));
@@ -624,6 +650,7 @@ describe("AutofillOverlayContentService", () => {
await autofillOverlayContentService.setupAutofillOverlayListenerOnField( await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
autofillFieldElement, autofillFieldElement,
autofillFieldData, autofillFieldData,
temporaryPageDetailsMock,
); );
autofillFieldElement.dispatchEvent(new Event("focus")); autofillFieldElement.dispatchEvent(new Event("focus"));
@@ -641,6 +668,7 @@ describe("AutofillOverlayContentService", () => {
await autofillOverlayContentService.setupAutofillOverlayListenerOnField( await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
autofillFieldElement, autofillFieldElement,
autofillFieldData, autofillFieldData,
temporaryPageDetailsMock,
); );
autofillFieldElement.dispatchEvent(new Event("focus")); autofillFieldElement.dispatchEvent(new Event("focus"));
@@ -660,6 +688,7 @@ describe("AutofillOverlayContentService", () => {
await autofillOverlayContentService.setupAutofillOverlayListenerOnField( await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
autofillFieldElement, autofillFieldElement,
autofillFieldData, autofillFieldData,
temporaryPageDetailsMock,
); );
autofillFieldElement.dispatchEvent(new Event("focus")); autofillFieldElement.dispatchEvent(new Event("focus"));
@@ -678,6 +707,7 @@ describe("AutofillOverlayContentService", () => {
await autofillOverlayContentService.setupAutofillOverlayListenerOnField( await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
autofillFieldElement, autofillFieldElement,
autofillFieldData, autofillFieldData,
temporaryPageDetailsMock,
); );
autofillFieldElement.dispatchEvent(new Event("focus")); autofillFieldElement.dispatchEvent(new Event("focus"));
@@ -695,6 +725,7 @@ describe("AutofillOverlayContentService", () => {
await autofillOverlayContentService.setupAutofillOverlayListenerOnField( await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
autofillFieldElement, autofillFieldElement,
autofillFieldData, autofillFieldData,
temporaryPageDetailsMock,
); );
autofillFieldElement.dispatchEvent(new Event("focus")); autofillFieldElement.dispatchEvent(new Event("focus"));
@@ -711,6 +742,7 @@ describe("AutofillOverlayContentService", () => {
await autofillOverlayContentService.setupAutofillOverlayListenerOnField( await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
autofillFieldElement, autofillFieldElement,
autofillFieldData, autofillFieldData,
temporaryPageDetailsMock,
); );
autofillFieldElement.dispatchEvent(new Event("focus")); autofillFieldElement.dispatchEvent(new Event("focus"));
@@ -733,6 +765,7 @@ describe("AutofillOverlayContentService", () => {
await autofillOverlayContentService.setupAutofillOverlayListenerOnField( await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
autofillFieldElement, autofillFieldElement,
autofillFieldData, autofillFieldData,
temporaryPageDetailsMock,
); );
expect(sendExtensionMessageSpy).toHaveBeenCalledWith("openAutofillOverlay"); expect(sendExtensionMessageSpy).toHaveBeenCalledWith("openAutofillOverlay");
@@ -747,6 +780,7 @@ describe("AutofillOverlayContentService", () => {
await autofillOverlayContentService.setupAutofillOverlayListenerOnField( await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
autofillFieldElement, autofillFieldElement,
autofillFieldData, autofillFieldData,
temporaryPageDetailsMock,
); );
expect(autofillOverlayContentService["mostRecentlyFocusedField"]).toEqual( expect(autofillOverlayContentService["mostRecentlyFocusedField"]).toEqual(
@@ -1613,6 +1647,7 @@ describe("AutofillOverlayContentService", () => {
autofillOverlayContentService.setupAutofillOverlayListenerOnField( autofillOverlayContentService.setupAutofillOverlayListenerOnField(
autofillFieldElement, autofillFieldElement,
autofillFieldData, autofillFieldData,
temporaryPageDetailsMock,
); );
autofillOverlayContentService["mostRecentlyFocusedField"] = autofillFieldElement; autofillOverlayContentService["mostRecentlyFocusedField"] = autofillFieldElement;
}); });

View File

@@ -7,6 +7,7 @@ import { EVENTS, AutofillOverlayVisibility } from "@bitwarden/common/autofill/co
import { FocusedFieldData } from "../background/abstractions/overlay.background"; import { FocusedFieldData } from "../background/abstractions/overlay.background";
import AutofillField from "../models/autofill-field"; import AutofillField from "../models/autofill-field";
import AutofillPageDetails from "../models/autofill-page-details";
import AutofillOverlayButtonIframe from "../overlay/iframe-content/autofill-overlay-button-iframe"; import AutofillOverlayButtonIframe from "../overlay/iframe-content/autofill-overlay-button-iframe";
import AutofillOverlayListIframe from "../overlay/iframe-content/autofill-overlay-list-iframe"; import AutofillOverlayListIframe from "../overlay/iframe-content/autofill-overlay-list-iframe";
import { ElementWithOpId, FillableFormFieldElement, FormFieldElement } from "../types"; import { ElementWithOpId, FillableFormFieldElement, FormFieldElement } from "../types";
@@ -23,8 +24,10 @@ import {
OpenAutofillOverlayOptions, OpenAutofillOverlayOptions,
} from "./abstractions/autofill-overlay-content.service"; } from "./abstractions/autofill-overlay-content.service";
import { AutoFillConstants } from "./autofill-constants"; import { AutoFillConstants } from "./autofill-constants";
import { InlineMenuFieldQualificationService } from "./inline-menu-field-qualification.service";
class AutofillOverlayContentService implements AutofillOverlayContentServiceInterface { class AutofillOverlayContentService implements AutofillOverlayContentServiceInterface {
private readonly inlineMenuFieldQualificationService: InlineMenuFieldQualificationService;
isFieldCurrentlyFocused = false; isFieldCurrentlyFocused = false;
isCurrentlyFilling = false; isCurrentlyFilling = false;
isOverlayCiphersPopulated = false; isOverlayCiphersPopulated = false;
@@ -62,6 +65,10 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte
zIndex: "2147483647", zIndex: "2147483647",
}; };
constructor() {
this.inlineMenuFieldQualificationService = new InlineMenuFieldQualificationService();
}
/** /**
* Initializes the autofill overlay content service by setting up the mutation observers. * Initializes the autofill overlay content service by setting up the mutation observers.
* The observers will be instantiated on DOMContentLoaded if the page is current loading. * The observers will be instantiated on DOMContentLoaded if the page is current loading.
@@ -81,12 +88,17 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte
* *
* @param formFieldElement - Form field elements identified during the page details collection process. * @param formFieldElement - Form field elements identified during the page details collection process.
* @param autofillFieldData - Autofill field data captured from the form field element. * @param autofillFieldData - Autofill field data captured from the form field element.
* @param pageDetails - The collected page details from the tab.
*/ */
async setupAutofillOverlayListenerOnField( async setupAutofillOverlayListenerOnField(
formFieldElement: ElementWithOpId<FormFieldElement>, formFieldElement: ElementWithOpId<FormFieldElement>,
autofillFieldData: AutofillField, autofillFieldData: AutofillField,
pageDetails: AutofillPageDetails,
) {
if (
this.formFieldElements.has(formFieldElement) ||
!this.inlineMenuFieldQualificationService.isFieldForLoginForm(autofillFieldData, pageDetails)
) { ) {
if (this.isIgnoredField(autofillFieldData) || this.formFieldElements.has(formFieldElement)) {
return; return;
} }
@@ -745,15 +757,15 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte
autofillFieldData.readonly || autofillFieldData.readonly ||
autofillFieldData.disabled || autofillFieldData.disabled ||
!autofillFieldData.viewable || !autofillFieldData.viewable ||
this.ignoredFieldTypes.has(autofillFieldData.type) || this.ignoredFieldTypes.has(autofillFieldData.type)
this.keywordsFoundInFieldData(autofillFieldData, ["search", "captcha"]) // || this.keywordsFoundInFieldData(autofillFieldData, ["search", "captcha"])
) { ) {
return true; return true;
} }
const isLoginCipherField = const isLoginCipherField =
autofillFieldData.type === "password" || this.inlineMenuFieldQualificationService.isCurrentPasswordField(autofillFieldData) ||
this.keywordsFoundInFieldData(autofillFieldData, AutoFillConstants.UsernameFieldNames); this.inlineMenuFieldQualificationService.isUsernameField(autofillFieldData);
return !isLoginCipherField; return !isLoginCipherField;
} }

View File

@@ -72,14 +72,17 @@ describe("CollectAutofillContentService", () => {
it("returns an object with empty forms and fields if no fields were found on a previous iteration", async () => { it("returns an object with empty forms and fields if no fields were found on a previous iteration", async () => {
collectAutofillContentService["domRecentlyMutated"] = false; collectAutofillContentService["domRecentlyMutated"] = false;
collectAutofillContentService["noFieldsFound"] = true; collectAutofillContentService["noFieldsFound"] = true;
jest.spyOn(collectAutofillContentService as any, "getFormattedPageDetails"); jest.spyOn(collectAutofillContentService as any, "buildFormattedPageDetails");
jest.spyOn(collectAutofillContentService as any, "queryAutofillFormAndFieldElements"); jest.spyOn(collectAutofillContentService as any, "queryAutofillFormAndFieldElements");
jest.spyOn(collectAutofillContentService as any, "buildAutofillFormsData"); jest.spyOn(collectAutofillContentService as any, "buildAutofillFormsData");
jest.spyOn(collectAutofillContentService as any, "buildAutofillFieldsData"); jest.spyOn(collectAutofillContentService as any, "buildAutofillFieldsData");
await collectAutofillContentService.getPageDetails(); await collectAutofillContentService.getPageDetails();
expect(collectAutofillContentService["getFormattedPageDetails"]).toHaveBeenCalledWith({}, []); expect(collectAutofillContentService["buildFormattedPageDetails"]).toHaveBeenCalledWith(
{},
[],
);
expect( expect(
collectAutofillContentService["queryAutofillFormAndFieldElements"], collectAutofillContentService["queryAutofillFormAndFieldElements"],
).not.toHaveBeenCalled(); ).not.toHaveBeenCalled();
@@ -156,7 +159,7 @@ describe("CollectAutofillContentService", () => {
collectAutofillContentService["autofillFieldElements"] = new Map([ collectAutofillContentService["autofillFieldElements"] = new Map([
[fieldElement, autofillField], [fieldElement, autofillField],
]); ]);
jest.spyOn(collectAutofillContentService as any, "getFormattedPageDetails"); jest.spyOn(collectAutofillContentService as any, "buildFormattedPageDetails");
jest.spyOn(collectAutofillContentService as any, "getFormattedAutofillFormsData"); jest.spyOn(collectAutofillContentService as any, "getFormattedAutofillFormsData");
jest.spyOn(collectAutofillContentService as any, "getFormattedAutofillFieldsData"); jest.spyOn(collectAutofillContentService as any, "getFormattedAutofillFieldsData");
jest.spyOn(collectAutofillContentService as any, "queryAutofillFormAndFieldElements"); jest.spyOn(collectAutofillContentService as any, "queryAutofillFormAndFieldElements");
@@ -165,7 +168,7 @@ describe("CollectAutofillContentService", () => {
await collectAutofillContentService.getPageDetails(); await collectAutofillContentService.getPageDetails();
expect(collectAutofillContentService["getFormattedPageDetails"]).toHaveBeenCalled(); expect(collectAutofillContentService["buildFormattedPageDetails"]).toHaveBeenCalled();
expect(collectAutofillContentService["getFormattedAutofillFormsData"]).toHaveBeenCalled(); expect(collectAutofillContentService["getFormattedAutofillFormsData"]).toHaveBeenCalled();
expect(collectAutofillContentService["getFormattedAutofillFieldsData"]).toHaveBeenCalled(); expect(collectAutofillContentService["getFormattedAutofillFieldsData"]).toHaveBeenCalled();
expect( expect(

View File

@@ -17,7 +17,6 @@ import {
elementIsSelectElement, elementIsSelectElement,
elementIsSpanElement, elementIsSpanElement,
nodeIsElement, nodeIsElement,
elementIsInputElement,
elementIsTextAreaElement, elementIsTextAreaElement,
nodeIsFormElement, nodeIsFormElement,
nodeIsInputElement, nodeIsInputElement,
@@ -46,6 +45,7 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
private mutationObserver: MutationObserver; private mutationObserver: MutationObserver;
private updateAutofillElementsAfterMutationTimeout: number | NodeJS.Timeout; private updateAutofillElementsAfterMutationTimeout: number | NodeJS.Timeout;
private mutationsQueue: MutationRecord[][] = []; private mutationsQueue: MutationRecord[][] = [];
private cachedAutofillPageDetails: AutofillPageDetails;
private readonly updateAfterMutationTimeoutDelay = 1000; private readonly updateAfterMutationTimeoutDelay = 1000;
private readonly formFieldQueryString; private readonly formFieldQueryString;
private readonly nonInputFormFieldTags = new Set(["textarea", "select"]); private readonly nonInputFormFieldTags = new Set(["textarea", "select"]);
@@ -95,13 +95,13 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
} }
if (!this.domRecentlyMutated && this.noFieldsFound) { if (!this.domRecentlyMutated && this.noFieldsFound) {
return this.getFormattedPageDetails({}, []); return this.buildFormattedPageDetails({}, []);
} }
if (!this.domRecentlyMutated && this.autofillFieldElements.size) { if (!this.domRecentlyMutated && this.autofillFieldElements.size) {
this.updateCachedAutofillFieldVisibility(); this.updateCachedAutofillFieldVisibility();
return this.getFormattedPageDetails( return this.buildFormattedPageDetails(
this.getFormattedAutofillFormsData(), this.getFormattedAutofillFormsData(),
this.getFormattedAutofillFieldsData(), this.getFormattedAutofillFieldsData(),
); );
@@ -120,7 +120,25 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
} }
this.domRecentlyMutated = false; this.domRecentlyMutated = false;
return this.getFormattedPageDetails(autofillFormsData, autofillFieldsData); const pageDetails = this.buildFormattedPageDetails(autofillFormsData, autofillFieldsData);
this.setupInlineMenuListeners(pageDetails);
return pageDetails;
}
private setupInlineMenuListeners(pageDetails: AutofillPageDetails) {
if (!this.autofillOverlayContentService) {
return;
}
const formFieldElements = Array.from(this.autofillFieldElements.keys());
for (const element of formFieldElements) {
void this.autofillOverlayContentService.setupAutofillOverlayListenerOnField(
element,
this.autofillFieldElements.get(element),
pageDetails,
);
}
} }
/** /**
@@ -256,11 +274,11 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
* @param autofillFormsData - The data for all the forms found in the page * @param autofillFormsData - The data for all the forms found in the page
* @param autofillFieldsData - The data for all the fields found in the page * @param autofillFieldsData - The data for all the fields found in the page
*/ */
private getFormattedPageDetails( private buildFormattedPageDetails(
autofillFormsData: Record<string, AutofillForm>, autofillFormsData: Record<string, AutofillForm>,
autofillFieldsData: AutofillField[], autofillFieldsData: AutofillField[],
): AutofillPageDetails { ): AutofillPageDetails {
return { this.cachedAutofillPageDetails = {
title: document.title, title: document.title,
url: (document.defaultView || globalThis).location.href, url: (document.defaultView || globalThis).location.href,
documentUrl: document.location.href, documentUrl: document.location.href,
@@ -268,6 +286,8 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
fields: autofillFieldsData, fields: autofillFieldsData,
collectedTimestamp: Date.now(), collectedTimestamp: Date.now(),
}; };
return this.cachedAutofillPageDetails;
} }
/** /**
@@ -453,10 +473,10 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
if (elementIsSpanElement(element)) { if (elementIsSpanElement(element)) {
this.cacheAutofillFieldElement(index, element, autofillFieldBase); this.cacheAutofillFieldElement(index, element, autofillFieldBase);
void this.autofillOverlayContentService?.setupAutofillOverlayListenerOnField( // void this.autofillOverlayContentService?.setupAutofillOverlayListenerOnField(
element, // element,
autofillFieldBase, // autofillFieldBase,
); // );
return autofillFieldBase; return autofillFieldBase;
} }
@@ -496,10 +516,10 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
}; };
this.cacheAutofillFieldElement(index, element, autofillField); this.cacheAutofillFieldElement(index, element, autofillField);
void this.autofillOverlayContentService?.setupAutofillOverlayListenerOnField( // void this.autofillOverlayContentService?.setupAutofillOverlayListenerOnField(
element, // element,
autofillField, // autofillField,
); // );
return autofillField; return autofillField;
}; };
@@ -1428,6 +1448,7 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
void this.autofillOverlayContentService?.setupAutofillOverlayListenerOnField( void this.autofillOverlayContentService?.setupAutofillOverlayListenerOnField(
formFieldElement, formFieldElement,
cachedAutofillFieldElement, cachedAutofillFieldElement,
this.cachedAutofillPageDetails,
); );
this.intersectionObserver?.unobserve(entry.target); this.intersectionObserver?.unobserve(entry.target);

View File

@@ -3,7 +3,7 @@ import AutofillPageDetails from "../models/autofill-page-details";
import { AutoFillConstants } from "./autofill-constants"; import { AutoFillConstants } from "./autofill-constants";
export class AutofillFieldQualificationService { export class InlineMenuFieldQualificationService {
private searchFieldNamesSet = new Set(AutoFillConstants.SearchFieldNames); private searchFieldNamesSet = new Set(AutoFillConstants.SearchFieldNames);
private excludedAutofillLoginTypesSet = new Set(AutoFillConstants.ExcludedAutofillLoginTypes); private excludedAutofillLoginTypesSet = new Set(AutoFillConstants.ExcludedAutofillLoginTypes);
private usernameFieldTypes = new Set(["text", "email", "tel"]); private usernameFieldTypes = new Set(["text", "email", "tel"]);
@@ -13,13 +13,8 @@ export class AutofillFieldQualificationService {
private invalidAutocompleteValuesSet = new Set(["off", "false"]); private invalidAutocompleteValuesSet = new Set(["off", "false"]);
isFieldForLoginForm(field: AutofillField, pageDetails: AutofillPageDetails): boolean { isFieldForLoginForm(field: AutofillField, pageDetails: AutofillPageDetails): boolean {
// TODO: Determine whether it makes sense to even incorporate this. const isCurrentPasswordField = this.isCurrentPasswordField(field);
if (!field.viewable) { if (isCurrentPasswordField) {
return false;
}
const isExistingPasswordField = this.isExistingPasswordField(field);
if (isExistingPasswordField) {
return this.isPasswordFieldForLoginForm(field, pageDetails); return this.isPasswordFieldForLoginForm(field, pageDetails);
} }
@@ -43,7 +38,7 @@ export class AutofillFieldQualificationService {
// Check if the field has a form parent // Check if the field has a form parent
const parentForm = pageDetails.forms[field.form]; const parentForm = pageDetails.forms[field.form];
const usernameFieldsInPageDetails = pageDetails.fields.filter(this.isUsernameField); const usernameFieldsInPageDetails = pageDetails.fields.filter(this.isUsernameField);
const passwordFieldsInPageDetails = pageDetails.fields.filter(this.isExistingPasswordField); const passwordFieldsInPageDetails = pageDetails.fields.filter(this.isCurrentPasswordField);
// If no form parent is found, check if a username field exists and no other password fields are found in the page details, if so treat this as a password field // If no form parent is found, check if a username field exists and no other password fields are found in the page details, if so treat this as a password field
if ( if (
!parentForm && !parentForm &&
@@ -100,7 +95,7 @@ export class AutofillFieldQualificationService {
// Check if the field has a form parent // Check if the field has a form parent
const parentForm = pageDetails.forms[field.form]; const parentForm = pageDetails.forms[field.form];
const passwordFieldsInPageDetails = pageDetails.fields.filter(this.isExistingPasswordField); const passwordFieldsInPageDetails = pageDetails.fields.filter(this.isCurrentPasswordField);
// console.log(passwordFieldsInPageDetails); // console.log(passwordFieldsInPageDetails);
// If no form parent is found, check if a single password field is found in the page details, if so treat this as a username field // If no form parent is found, check if a single password field is found in the page details, if so treat this as a username field
@@ -177,7 +172,7 @@ export class AutofillFieldQualificationService {
return this.keywordsFoundInFieldData(field, AutoFillConstants.UsernameFieldNames); return this.keywordsFoundInFieldData(field, AutoFillConstants.UsernameFieldNames);
}; };
isExistingPasswordField = (field: AutofillField): boolean => { isCurrentPasswordField = (field: AutofillField): boolean => {
if (field.autoCompleteType === "new-password") { if (field.autoCompleteType === "new-password") {
return false; return false;
} }