password-store-extension/extension/background.js

170 lines
3.7 KiB
JavaScript

const tabForms = {};
const tabMatches = {};
let lastTabId = null;
let currentPageForms = null;
let currentPageMatches = null;
let addedContexts = [];
function askNative(msg) {
const port = chrome.runtime.connectNative('ee.lunasqu.password_manager');
return new Promise((resolve, reject) => {
let gotResponse = false;
port.onMessage.addListener((msg) => {
gotResponse = true;
resolve(msg);
port.disconnect();
});
port.onDisconnect.addListener((msg) => {
if (!gotResponse) {
gotResponse = true;
reject(msg);
}
});
port.postMessage(msg);
});
}
chrome.runtime.onInstalled.addListener(() => {
chrome.contextMenus.create({
id: 'fillContextMenu',
title: 'Autofill password',
contexts: ['editable'],
});
});
function addContextMenuItems() {
if (!currentPageMatches) {
return;
}
clearContextMenu();
for (const match of currentPageMatches) {
const ctxID = `fill-${match}`;
chrome.contextMenus.create({
id: ctxID,
title: match,
contexts: ['editable'],
parentId: 'fillContextMenu',
});
addedContexts.push(ctxID);
}
}
function clearContextMenu() {
addedContexts.forEach((id) => {
try {
chrome.contextMenus.remove(id);
} catch (e) {
console.error(e);
}
});
addedContexts.length = 0;
}
async function injectForeground() {
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
if (!tab || !tab.url) {
return;
}
lastTabId = tab.id;
await chrome.scripting.executeScript({
target: { tabId: tab.id },
files: ['./foreground.js'],
});
const parsed = new URL(tab.url);
askNative({
domain: parsed.hostname,
}).then(({ results }) => {
if (results && results.length) {
currentPageMatches = results;
tabMatches[tab.id] = results;
chrome.tabs.sendMessage(tab.id, {
message: 'has_entries',
payload: results,
});
addContextMenuItems();
}
});
}
async function sendPasswordToTab(rdata, existingTab) {
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
const realTab = existingTab || tab;
if (!realTab || !realTab.url) {
return;
}
chrome.tabs.sendMessage(realTab.id, {
message: 'fill_password',
payload: rdata,
});
}
chrome.webNavigation.onCompleted.addListener((ee) => {
if (ee.frameType !== 'outermost_frame') {
return;
}
currentPageForms = null;
currentPageMatches = null;
injectForeground();
});
chrome.tabs.onActivated.addListener(function (activeInfo) {
lastTabId = activeInfo.tabId;
currentPageForms = tabForms[activeInfo.tabId];
currentPageMatches = tabMatches[activeInfo.tabId];
clearContextMenu();
if (currentPageMatches) {
addContextMenuItems();
}
});
chrome.tabs.onRemoved.addListener(function (tabId, info) {
delete tabForms[tabId];
delete tabMatches[tabId];
});
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.message === 'login_forms') {
if (sender && sender.tab && sender.tab.id) {
tabForms[sender.tab.id] = request.payload;
}
if (sender.tab.id === lastTabId) {
currentPageForms = request.payload;
}
sendResponse(true);
}
if (request.message === 'autofill') {
askNative({
getPassword: request.payload,
}).then((response) => {
sendPasswordToTab(response);
});
sendResponse(true);
}
});
chrome.contextMenus.onClicked.addListener((info, tab) => {
if (info && info.menuItemId && info.menuItemId.startsWith('fill-')) {
const fillId = info.menuItemId.substring(5);
askNative({
getPassword: fillId,
}).then((response) => {
sendPasswordToTab(response, tab);
});
}
});