diff --git a/browser-extension/src/entrypoints/content.ts b/browser-extension/src/entrypoints/content.ts index 4654f02f6..f625b7956 100644 --- a/browser-extension/src/entrypoints/content.ts +++ b/browser-extension/src/entrypoints/content.ts @@ -22,116 +22,88 @@ export default defineContentScript({ return; } - let ui: Awaited> | null = null; - - /** - * Handle input field focus. - */ - const handleFocusIn = async (e: FocusEvent) : Promise => { - 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 => { + 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 => { - 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 => { + 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(); }, }); \ No newline at end of file