Make browser extension autofill dismiss button more reliable (#797)

This commit is contained in:
Leendert de Borst
2025-04-15 11:51:21 +02:00
committed by Leendert de Borst
parent 8b835a4a77
commit 22d2e09982
3 changed files with 57 additions and 27 deletions

View File

@@ -315,27 +315,7 @@ export function createAutofillPopup(input: HTMLInputElement, credentials: Creden
};
// Add click listener with capture and prevent removal.
createButton.addEventListener('click', handleCreateClick, {
capture: true,
passive: false
});
// Backup click handling using mousedown/mouseup if needed.
let isMouseDown = false;
createButton.addEventListener('mousedown', (e) => {
e.preventDefault();
e.stopPropagation();
isMouseDown = true;
}, { capture: true });
createButton.addEventListener('mouseup', (e) => {
e.preventDefault();
e.stopPropagation();
if (isMouseDown) {
handleCreateClick(e);
}
isMouseDown = false;
}, { capture: true });
addReliableClickHandler(createButton, handleCreateClick);
// Create search input.
const searchInput = document.createElement('input');
@@ -358,10 +338,18 @@ export function createAutofillPopup(input: HTMLInputElement, credentials: Creden
</svg>
`;
closeButton.addEventListener('click', async () => {
/**
* Handle close button click
*/
const handleCloseClick = async (e: Event) : Promise<void> => {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
await disableAutoShowPopup();
removeExistingPopup(rootContainer);
});
};
addReliableClickHandler(closeButton, handleCloseClick);
actionContainer.appendChild(searchInput);
actionContainer.appendChild(createButton);
@@ -1378,3 +1366,35 @@ function getValidServiceUrl(): string {
return '';
}
}
/**
* Add click handler with mousedown/mouseup backup for better click reliability in shadow DOM.
*
* Some websites due to their design cause the AliasVault autofill to re-trigger when clicking
* outside of the input field, which causes the AliasVault popup to close before the click event
* is registered. This is a workaround to ensure the click event is always registered.
*/
function addReliableClickHandler(element: HTMLElement, handler: (e: Event) => void): void {
// Add primary click listener with capture and prevent removal
element.addEventListener('click', handler, {
capture: true,
passive: false
});
// Backup click handling using mousedown/mouseup if needed
let isMouseDown = false;
element.addEventListener('mousedown', (e) => {
e.preventDefault();
e.stopPropagation();
isMouseDown = true;
}, { capture: true });
element.addEventListener('mouseup', (e) => {
e.preventDefault();
e.stopPropagation();
if (isMouseDown) {
handler(e);
}
isMouseDown = false;
}, { capture: true });
}

View File

@@ -46,7 +46,7 @@ export class FormDetector {
}
return true;
}
// Check for display:none
if (style.display === 'none') {
// Cache and return false for this element and all its parents
@@ -57,7 +57,7 @@ export class FormDetector {
}
return false;
}
// Check for visibility:hidden
if (style.visibility === 'hidden') {
// Cache and return false for this element and all its parents
@@ -68,7 +68,7 @@ export class FormDetector {
}
return false;
}
// Check for opacity:0
if (parseFloat(style.opacity) === 0) {
// Cache and return false for this element and all its parents
@@ -101,7 +101,16 @@ export class FormDetector {
* Detect login forms on the page based on the clicked element.
*/
public containsLoginForm(): boolean {
const formWrapper = this.clickedElement?.closest('form') ?? this.document.body;
let formWrapper = this.clickedElement?.closest('form, [role="dialog"]') as HTMLElement | null;
if (formWrapper?.getAttribute('role') === 'dialog') {
// If we hit a dialog, search for form only within the dialog
formWrapper = formWrapper.querySelector('form') as HTMLElement | null ?? formWrapper;
}
if (!formWrapper) {
// If no form or dialog found, fallback to document.body
formWrapper = this.document.body as HTMLElement;
}
/**
* Sanity check: if form contains more than 150 inputs, don't process as this is likely not a login form.

View File

@@ -112,4 +112,5 @@ The following websites have been known to cause issues in the past (but should b
| 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 |
| https://mijn.ing.nl/login/ | Autofill doesn't detect input fields and AliasVault autofill icon placement is off |
| https://github.com/lanedirt/AliasVault/issues | The "New issue -> Blank Issue" title field causes the autofill to trigger because of a parent form (outside of the role=modal div) |