diff --git a/browser-extension/src/entrypoints/contentScript/Form.ts b/browser-extension/src/entrypoints/contentScript/Form.ts index ac67a686c..812f845a5 100644 --- a/browser-extension/src/entrypoints/contentScript/Form.ts +++ b/browser-extension/src/entrypoints/contentScript/Form.ts @@ -161,51 +161,53 @@ export function injectIcon(input: HTMLInputElement, container: HTMLElement): voi * Trigger input events for an element to trigger form validation * which some websites require before the "continue" button is enabled. */ -function triggerInputEvents(element: HTMLInputElement | HTMLSelectElement) : void { - // Create an overlay div that will show the highlight effect - const overlay = document.createElement('div'); +function triggerInputEvents(element: HTMLInputElement | HTMLSelectElement, animate: boolean = true) : void { + // Add keyframe animation if animation is requested + if (animate) { + // Create an overlay div that will show the highlight effect + const overlay = document.createElement('div'); - /** - * Update position of the overlay. - */ - const updatePosition = () : void => { - const rect = element.getBoundingClientRect(); - overlay.style.cssText = ` - position: fixed; - z-index: 999999991; - pointer-events: none; - top: ${rect.top}px; - left: ${rect.left}px; - width: ${rect.width}px; - height: ${rect.height}px; - background-color: rgba(244, 149, 65, 0.3); - border-radius: ${getComputedStyle(element).borderRadius}; - animation: fadeOut 1.4s ease-out forwards; + /** + * Update position of the overlay. + */ + const updatePosition = () : void => { + const rect = element.getBoundingClientRect(); + overlay.style.cssText = ` + position: fixed; + z-index: 999999991; + pointer-events: none; + top: ${rect.top}px; + left: ${rect.left}px; + width: ${rect.width}px; + height: ${rect.height}px; + background-color: rgba(244, 149, 65, 0.3); + border-radius: ${getComputedStyle(element).borderRadius}; + animation: fadeOut 1.4s ease-out forwards; + `; + }; + + updatePosition(); + + // Add scroll event listener + window.addEventListener('scroll', updatePosition); + + const style = document.createElement('style'); + style.textContent = ` + @keyframes fadeOut { + 0% { opacity: 1; transform: scale(1.02); } + 100% { opacity: 0; transform: scale(1); } + } `; - }; + document.head.appendChild(style); + document.body.appendChild(overlay); - updatePosition(); - - // Add scroll event listener - window.addEventListener('scroll', updatePosition); - - // Add keyframe animation - const style = document.createElement('style'); - style.textContent = ` - @keyframes fadeOut { - 0% { opacity: 1; transform: scale(1.02); } - 100% { opacity: 0; transform: scale(1); } - } - `; - document.head.appendChild(style); - document.body.appendChild(overlay); - - // Remove overlay and cleanup after animation - setTimeout(() => { - window.removeEventListener('scroll', updatePosition); - overlay.remove(); - style.remove(); - }, 1400); + // Remove overlay and cleanup after animation + setTimeout(() => { + window.removeEventListener('scroll', updatePosition); + overlay.remove(); + style.remove(); + }, 1400); + } // Trigger events element.dispatchEvent(new Event('input', { bubbles: true })); diff --git a/browser-extension/src/utils/formDetector/FormFiller.ts b/browser-extension/src/utils/formDetector/FormFiller.ts index 4aacfd523..e998bfed7 100644 --- a/browser-extension/src/utils/formDetector/FormFiller.ts +++ b/browser-extension/src/utils/formDetector/FormFiller.ts @@ -11,8 +11,13 @@ export class FormFiller { */ public constructor( private readonly form: FormFields, - private readonly triggerInputEvents: (element: HTMLInputElement | HTMLSelectElement) => void - ) {} + private readonly triggerInputEvents: (element: HTMLInputElement | HTMLSelectElement, animate?: boolean) => void + ) { + /** + * Trigger input events. + */ + this.triggerInputEvents = (element: HTMLInputElement | HTMLSelectElement, animate = true) : void => triggerInputEvents(element, animate); + } /** * Fill the fields of the form with the given credential. @@ -72,7 +77,7 @@ export class FormFiller { /** * Fill the password field with the given password. This uses a small delay between each character to simulate human typing. - * In the past there have been issues where Microsoft 365 login forms would clear the password field when just setting the value directly. + * Simulates actual keystroke behavior by appending characters one by one. * * @param field The password field to fill. * @param password The password to fill the field with. @@ -80,14 +85,17 @@ export class FormFiller { private async fillPasswordField(field: HTMLInputElement, password: string): Promise { // Clear the field first field.value = ''; - this.triggerInputEvents(field); + this.triggerInputEvents(field, false); // Type each character with a small delay - for (let i = 0; i < password.length; i++) { - field.value = password.substring(0, i + 1); + for (const char of password) { + // Append the character to the current value instead of using substring + field.value += char; // Small random delay between 5-15ms to simulate human typing await new Promise(resolve => setTimeout(resolve, Math.random() * 10 + 5)); } + + this.triggerInputEvents(field, false); } /** diff --git a/docs/misc/dev/browser-extensions.md b/docs/misc/dev/browser-extensions.md index c50d9c7cf..353bfe00d 100644 --- a/docs/misc/dev/browser-extensions.md +++ b/docs/misc/dev/browser-extensions.md @@ -109,3 +109,5 @@ The following websites have been known to cause issues in the past (but should b | https://bloshing.com/inschrijven-nieuwsbrief | Popup CSS style conflicts | | https://gamefaqs.gamespot.com/user | Popup buttons not working | | https://news.ycombinator.com/login?goto=news | Popup and client favicon not showing due to SVG format | +| https://vault.bitwarden.com/#/login | Autofill password not detected (input not long enough), manually typing in works | +| https://login.microsoftonline.com/ | Password gets reset after autofill |