230 lines
5.9 KiB
JavaScript
230 lines
5.9 KiB
JavaScript
(() => {
|
|
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();
|
|
}
|
|
});
|
|
})();
|