(() => { let currentPageMatches = null; let currentForm = null; let successfulAttachment = false; let focusedField = null; function generateQuerySelector(el) { if (el.tagName.toLowerCase() === 'html') { return 'HTML'; } let str = el.tagName; str += el.id != '' ? '#' + el.id : ''; if (el.className) { const classes = el.className.split(/\s/); for (let i = 0; i < classes.length; i++) { str += '.' + classes[i]; } } return generateQuerySelector(el.parentNode) + ' > ' + str; } function lookForLoginForms() { const allPasswordInputs = document.querySelectorAll( 'input[type="password"]' ); let loginForms = []; allPasswordInputs.forEach((field) => { const closestForm = field.closest('form'); if (!closestForm) { return; } const existing = loginForms.find(({ form }) => form === closestForm); if (existing) { existing.passwordAgain = field; return; } let usernameField; const allInputs = Array.from(closestForm.querySelectorAll('input')); const contenders = allInputs.filter((element) => { const nameAttr = (element.getAttribute('name') || '').toLowerCase(); const idAttr = (element.getAttribute('id') || '').toLowerCase(); const closestLabel = closestForm.querySelector( `label[for="${idAttr}"]` ); if (closestLabel) { const labelText = closestLabel.innerText.toLowerCase().trim(); if ( labelText.startsWith('user') || labelText.startsWith('email') || labelText.startsWith('e-mail') ) { return true; } } if (nameAttr.includes('username')) { return true; } if (nameAttr.includes('email')) { return true; } if (nameAttr.includes('name')) { return true; } }); usernameField = contenders[0]; loginForms.push({ password: field, username: usernameField, form: closestForm, }); }); if (loginForms.length) { chrome.runtime.sendMessage({ message: 'login_forms', payload: loginForms.map((stringify) => ({ form: generateQuerySelector(stringify.form), password: generateQuerySelector(stringify.password), passwordAgain: stringify.passwordAgain ? generateQuerySelector(stringify.passwordAgain) : null, username: stringify.username ? generateQuerySelector(stringify.username) : null, })), }); } return loginForms; } function createOptionsSelect(commitAutofill) { const select = document.createElement('select'); const unopt = document.createElement('option'); unopt.innerText = 'Select autofill...'; select.appendChild(unopt); Object.assign(select.style, { pointerEvents: 'all', }); currentPageMatches.forEach((match) => { const option = document.createElement('option'); option.value = match; option.innerText = match; select.appendChild(option); }); select.addEventListener('change', function () { const val = select.value; if (val) { commitAutofill(val); } }); return select; } function attachLoginFormHighlight(info) { successfulAttachment = true; const autoFillContainer = document.createElement('div'); Object.assign(autoFillContainer.style, { position: 'absolute', outline: '10px solid rgb(0 170 255 / 60%)', boxSizing: 'border-box', pointerEvents: 'none', borderRadius: '10px', zIndex: '10000000', }); document.body.appendChild(autoFillContainer); function reposition() { const boundingBox = info.form.getBoundingClientRect(); Object.assign(autoFillContainer.style, { top: `${boundingBox.y - 10 + window.scrollY}px`, left: `${boundingBox.x - 10}px`, width: `${boundingBox.width + 20}px`, height: `${boundingBox.height + 20}px`, }); } window.addEventListener('resize', reposition); window.addEventListener('scroll', reposition); reposition(); const select = createOptionsSelect((password) => { currentForm = info; chrome.runtime.sendMessage({ message: 'autofill', payload: password, }); }); autoFillContainer.appendChild(select); } function init() { const forms = lookForLoginForms(); if (forms.length) { forms.forEach((form) => attachLoginFormHighlight(form)); } } function fakeTriggers(input, value) { const inputEvt = new Event('input'); const changeEvt = new Event('change'); input.focus(); input.value = value; input.setAttribute('value', value); input.dispatchEvent(inputEvt); input.dispatchEvent(changeEvt); input.blur(); } chrome.runtime.onMessage.addListener(function ( request, sender, sendResponse ) { if (request.message === 'has_entries') { currentPageMatches = request.payload; init(); sendResponse(true); } if (request.message === 'fill_password') { if (request && request.payload.password) { if (currentForm) { fakeTriggers(currentForm.password, request.payload.password); if (currentForm.username && request.payload.username) { fakeTriggers(currentForm.username, request.payload.username); } setTimeout(() => currentForm.password.focus(), 100); } else if (focusedField) { fakeTriggers(focusedField, request.payload.password); } } sendResponse(true); } }); document.addEventListener('click', function (e) { if (!currentPageMatches) { return; } focusedField = e.target; if (successfulAttachment) { return; } if (e.target && e.target.tagName === 'INPUT') { init(); } }); })();