From 50cef27947bfc04a5f3f54a9d44b5d108cbd08c0 Mon Sep 17 00:00:00 2001 From: Jonathan Prusik Date: Wed, 15 Oct 2025 17:05:03 -0400 Subject: [PATCH 1/3] ensure autofillInsertActions execution order is preserved --- .../src/autofill/services/insert-autofill-content.service.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/browser/src/autofill/services/insert-autofill-content.service.ts b/apps/browser/src/autofill/services/insert-autofill-content.service.ts index 6034563a947f..e87e93f280f9 100644 --- a/apps/browser/src/autofill/services/insert-autofill-content.service.ts +++ b/apps/browser/src/autofill/services/insert-autofill-content.service.ts @@ -49,8 +49,9 @@ class InsertAutofillContentService implements InsertAutofillContentServiceInterf return; } - const fillActionPromises = fillScript.script.map(this.runFillScriptAction); - await Promise.all(fillActionPromises); + for (let index = 0; index < fillScript.script.length; index++) { + await this.runFillScriptAction(fillScript.script[index], index); + } } /** From 3f0608e4a8f69f1525216cacb4179bb7af1c9284 Mon Sep 17 00:00:00 2001 From: Jonathan Prusik Date: Wed, 15 Oct 2025 18:15:25 -0400 Subject: [PATCH 2/3] don't fill a field if it already has the value that is going to be filled --- .../src/autofill/services/insert-autofill-content.service.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/browser/src/autofill/services/insert-autofill-content.service.ts b/apps/browser/src/autofill/services/insert-autofill-content.service.ts index e87e93f280f9..172368038b6d 100644 --- a/apps/browser/src/autofill/services/insert-autofill-content.service.ts +++ b/apps/browser/src/autofill/services/insert-autofill-content.service.ts @@ -190,10 +190,14 @@ class InsertAutofillContentService implements InsertAutofillContentServiceInterf const elementCanBeReadonly = elementIsInputElement(element) || elementIsTextAreaElement(element); const elementCanBeFilled = elementCanBeReadonly || elementIsSelectElement(element); + const elementValue = (element as HTMLInputElement).value || element.innerText || ""; + + const elementAlreadyHasTheValue = !!(elementValue?.length && elementValue === value); if ( !element || !value || + elementAlreadyHasTheValue || (elementCanBeReadonly && element.readOnly) || (elementCanBeFilled && element.disabled) ) { From 5b97f8a9ac7b4a37795c8518aeaabaeb4da2d156 Mon Sep 17 00:00:00 2001 From: Jonathan Prusik Date: Fri, 17 Oct 2025 09:31:50 -0400 Subject: [PATCH 3/3] update tests --- .../insert-autofill-content.service.spec.ts | 52 +++++++++++++++---- .../insert-autofill-content.service.ts | 2 +- 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/apps/browser/src/autofill/services/insert-autofill-content.service.spec.ts b/apps/browser/src/autofill/services/insert-autofill-content.service.spec.ts index 9edcdbb3a95e..07fdfb9db790 100644 --- a/apps/browser/src/autofill/services/insert-autofill-content.service.spec.ts +++ b/apps/browser/src/autofill/services/insert-autofill-content.service.spec.ts @@ -103,7 +103,7 @@ describe("InsertAutofillContentService", () => { delay_between_operations: 20, }, metadata: {}, - autosubmit: null, + autosubmit: [], savedUrls: ["https://bitwarden.com"], untrustedIframe: false, itemType: "login", @@ -218,28 +218,21 @@ describe("InsertAutofillContentService", () => { await insertAutofillContentService.fillForm(fillScript); - expect(insertAutofillContentService["userCancelledInsecureUrlAutofill"]).toHaveBeenCalled(); - expect( - insertAutofillContentService["userCancelledUntrustedIframeAutofill"], - ).toHaveBeenCalled(); expect(insertAutofillContentService["runFillScriptAction"]).toHaveBeenCalledTimes(3); expect(insertAutofillContentService["runFillScriptAction"]).toHaveBeenNthCalledWith( 1, fillScript.script[0], 0, - fillScript.script, ); expect(insertAutofillContentService["runFillScriptAction"]).toHaveBeenNthCalledWith( 2, fillScript.script[1], 1, - fillScript.script, ); expect(insertAutofillContentService["runFillScriptAction"]).toHaveBeenNthCalledWith( 3, fillScript.script[2], 2, - fillScript.script, ); }); }); @@ -623,7 +616,30 @@ describe("InsertAutofillContentService", () => { }); }); - it("will set the `value` attribute of any passed input or textarea elements", () => { + it("will set the `value` attribute of any passed input or textarea elements if the value differs", () => { + document.body.innerHTML = ``; + const value1 = "test"; + const value2 = "test2"; + const textInputElement = document.getElementById("username") as HTMLInputElement; + const textareaElement = document.getElementById("bio") as HTMLTextAreaElement; + jest.spyOn(insertAutofillContentService as any, "handleInsertValueAndTriggerSimulatedEvents"); + + insertAutofillContentService["insertValueIntoField"](textInputElement, value1); + + expect(textInputElement.value).toBe(value1); + expect( + insertAutofillContentService["handleInsertValueAndTriggerSimulatedEvents"], + ).toHaveBeenCalledWith(textInputElement, expect.any(Function)); + + insertAutofillContentService["insertValueIntoField"](textareaElement, value2); + + expect(textareaElement.value).toBe(value2); + expect( + insertAutofillContentService["handleInsertValueAndTriggerSimulatedEvents"], + ).toHaveBeenCalledWith(textareaElement, expect.any(Function)); + }); + + it("will NOT set the `value` attribute of any passed input or textarea elements if they already have values matching the passed value", () => { document.body.innerHTML = ``; const value1 = "test"; const value2 = "test2"; @@ -638,14 +654,28 @@ describe("InsertAutofillContentService", () => { expect(textInputElement.value).toBe(value1); expect( insertAutofillContentService["handleInsertValueAndTriggerSimulatedEvents"], - ).toHaveBeenCalledWith(textInputElement, expect.any(Function)); + ).not.toHaveBeenCalled(); insertAutofillContentService["insertValueIntoField"](textareaElement, value2); expect(textareaElement.value).toBe(value2); expect( insertAutofillContentService["handleInsertValueAndTriggerSimulatedEvents"], - ).toHaveBeenCalledWith(textareaElement, expect.any(Function)); + ).not.toHaveBeenCalled(); + }); + + it("skips filling when the field already has the target value", () => { + const value = "test"; + document.body.innerHTML = ``; + const element = document.getElementById("username") as FillableFormFieldElement; + jest.spyOn(insertAutofillContentService as any, "handleInsertValueAndTriggerSimulatedEvents"); + + insertAutofillContentService["insertValueIntoField"](element, value); + + expect( + insertAutofillContentService["handleInsertValueAndTriggerSimulatedEvents"], + ).not.toHaveBeenCalled(); + expect(element.value).toBe(value); }); }); diff --git a/apps/browser/src/autofill/services/insert-autofill-content.service.ts b/apps/browser/src/autofill/services/insert-autofill-content.service.ts index 172368038b6d..9ddbcdc005d0 100644 --- a/apps/browser/src/autofill/services/insert-autofill-content.service.ts +++ b/apps/browser/src/autofill/services/insert-autofill-content.service.ts @@ -190,7 +190,7 @@ class InsertAutofillContentService implements InsertAutofillContentServiceInterf const elementCanBeReadonly = elementIsInputElement(element) || elementIsTextAreaElement(element); const elementCanBeFilled = elementCanBeReadonly || elementIsSelectElement(element); - const elementValue = (element as HTMLInputElement).value || element.innerText || ""; + const elementValue = (element as HTMLInputElement)?.value || element?.innerText || ""; const elementAlreadyHasTheValue = !!(elementValue?.length && elementValue === value);