Attach shadowroot to html immediately instead as waiting for element doesn't work (#756)

This commit is contained in:
Leendert de Borst
2025-04-02 16:59:56 +02:00
committed by Leendert de Borst
parent 60ba96cb86
commit 41d6511eb2

View File

@@ -22,116 +22,88 @@ export default defineContentScript({
return;
}
let ui: Awaited<ReturnType<typeof createShadowRootUi>> | null = null;
/**
* Handle input field focus.
*/
const handleFocusIn = async (e: FocusEvent) : Promise<void> => {
if (ctx.isInvalid) {
return;
}
// Check if element itself, html or body has av-disable attribute like av-disable="true"
const avDisable = (e.target as HTMLElement).getAttribute('av-disable') ?? document.body?.getAttribute('av-disable') ?? document.documentElement.getAttribute('av-disable');
if (avDisable === 'true') {
return;
}
const target = e.target as HTMLInputElement;
const textInputTypes = ['text', 'email', 'tel', 'password', 'search', 'url'];
if (target.tagName === 'INPUT' && textInputTypes.includes(target.type) && !target.dataset.aliasvaultIgnore) {
const formDetector = new FormDetector(document, target);
if (!formDetector.containsLoginForm()) {
return;
}
// Create shadow root UI only if it doesn't exist
if (!ui) {
ui = await createShadowRootUi(ctx, {
name: 'aliasvault-ui',
position: 'inline',
anchor: target,
/**
* Inject the icon and potentially show popup.
*
* @param container - The container element.
*/
async onMount(container) {
injectIcon(target, container);
// Only show popup if its enabled and debounce time has passed.
if (await isAutoShowPopupEnabled() && popupDebounceTimeHasPassed()) {
openAutofillPopup(target, container);
}
},
});
} else {
// If UI exists, just inject the icon and potentially show popup
injectIcon(target, ui.uiContainer);
if (await isAutoShowPopupEnabled() && popupDebounceTimeHasPassed()) {
openAutofillPopup(target, ui.uiContainer);
// Create a shadow root UI for isolation
const ui = await createShadowRootUi(ctx, {
name: 'aliasvault-ui',
position: 'inline',
anchor: 'html',
/**
* Handle mount.
*/
onMount(container) {
/**
* Handle input field focus.
*/
const handleFocusIn = async (e: FocusEvent) : Promise<void> => {
if (ctx.isInvalid) {
return;
}
}
}
};
// Listen for input field focus in the main document
document.addEventListener('focusin', handleFocusIn);
// Check if element itself, html or body has av-disable attribute like av-disable="true"
const avDisable = (e.target as HTMLElement).getAttribute('av-disable') ?? document.body?.getAttribute('av-disable') ?? document.documentElement.getAttribute('av-disable');
if (avDisable === 'true') {
return;
}
// Listen for popstate events (back/forward navigation)
window.addEventListener('popstate', () => {
if (ctx.isInvalid || !ui) {
return;
}
const target = e.target as HTMLInputElement;
const textInputTypes = ['text', 'email', 'tel', 'password', 'search', 'url'];
removeExistingPopup(ui.uiContainer);
});
if (target.tagName === 'INPUT' && textInputTypes.includes(target.type) && !target.dataset.aliasvaultIgnore) {
const formDetector = new FormDetector(document, target);
if (!formDetector.containsLoginForm()) {
return;
}
// Listen for messages from the background script
onMessage('OPEN_AUTOFILL_POPUP', async (message: { data: { elementIdentifier: string } }) : Promise<messageBoolResponse> => {
const { data } = message;
const { elementIdentifier } = data;
if (!elementIdentifier) {
return { success: false, error: 'No element identifier provided' };
}
const target = document.getElementById(elementIdentifier) ?? document.getElementsByName(elementIdentifier)[0];
if (!(target instanceof HTMLInputElement)) {
return { success: false, error: 'Target element is not an input field' };
}
const formDetector = new FormDetector(document, target);
if (!formDetector.containsLoginForm()) {
return { success: false, error: 'No form found' };
}
// Create shadow root UI if it doesn't exist
if (!ui) {
ui = await createShadowRootUi(ctx, {
name: 'aliasvault-ui',
position: 'inline',
anchor: target,
/**
* Inject the icon and show popup.
*
* @param container - The container element.
*/
async onMount(container) {
injectIcon(target, container);
openAutofillPopup(target, container);
},
});
} else {
injectIcon(target, ui.uiContainer);
openAutofillPopup(target, ui.uiContainer);
}
return { success: true };
// Only show popup if its enabled and debounce time has passed.
if (await isAutoShowPopupEnabled() && popupDebounceTimeHasPassed()) {
openAutofillPopup(target, container);
}
}
};
// Listen for input field focus in the main document
document.addEventListener('focusin', handleFocusIn);
// Listen for popstate events (back/forward navigation)
window.addEventListener('popstate', () => {
if (ctx.isInvalid) {
return;
}
removeExistingPopup(container);
});
// Listen for messages from the background script
onMessage('OPEN_AUTOFILL_POPUP', async (message: { data: { elementIdentifier: string } }) : Promise<messageBoolResponse> => {
const { data } = message;
const { elementIdentifier } = data;
if (!elementIdentifier) {
return { success: false, error: 'No element identifier provided' };
}
const target = document.getElementById(elementIdentifier) ?? document.getElementsByName(elementIdentifier)[0];
if (!(target instanceof HTMLInputElement)) {
return { success: false, error: 'Target element is not an input field' };
}
const formDetector = new FormDetector(document, target);
if (!formDetector.containsLoginForm()) {
return { success: false, error: 'No form found' };
}
injectIcon(target, container);
openAutofillPopup(target, container);
return { success: true };
});
},
});
// Mount the UI to create the shadow root
ui.autoMount();
},
});