mirror of
https://github.com/keepassxreboot/keepassxc-browser.git
synced 2026-03-11 08:54:43 +00:00
Allow filling KPH attributes from context menu
Co-authored-by: varjolintu <sami.vanttinen@protonmail.com>
This commit is contained in:
parent
376510c3f8
commit
08380827c2
5 changed files with 93 additions and 2 deletions
|
|
@ -27,6 +27,10 @@
|
|||
"message": "Show Password Generator",
|
||||
"description": "Show password generator text."
|
||||
},
|
||||
"contextMenuFillAttribute": {
|
||||
"message": "Fill attribute…",
|
||||
"description": "Context menu parent item for filling a custom attribute."
|
||||
},
|
||||
"multipleCredentialsDetected": {
|
||||
"message": "HTTP authentication with multiple credentials detected. Click on the extension icon to choose the correct one.",
|
||||
"description": "Notification when HTTP Authentication has multiple credentials detected."
|
||||
|
|
|
|||
|
|
@ -236,5 +236,6 @@ kpxcEvent.messageHandlers = {
|
|||
'update_credentials': keepass.updateCredentials,
|
||||
'username_field_detected': kpxcEvent.onUsernameFieldDetected,
|
||||
'save_settings': kpxcEvent.onSaveSettings,
|
||||
'update_available_keepassxc': kpxcEvent.onUpdateAvailableKeePassXC
|
||||
'update_available_keepassxc': kpxcEvent.onUpdateAvailableKeePassXC,
|
||||
'update_context_menu': page.updateContextMenu
|
||||
};
|
||||
|
|
|
|||
|
|
@ -107,6 +107,7 @@ const contextMenuItems = [
|
|||
{ title: tr('contextMenuFillUsernameAndPassword'), action: 'fill_username_password' },
|
||||
{ title: tr('contextMenuFillPassword'), action: 'fill_password' },
|
||||
{ title: tr('contextMenuFillTOTP'), action: 'fill_totp' },
|
||||
{ title: tr('contextMenuFillAttribute'), id: 'fill_attribute', visible: false },
|
||||
{ title: tr('contextMenuShowPasswordGenerator'), action: 'show_password_generator' },
|
||||
{ title: tr('contextMenuSaveCredentials'), action: 'remember_credentials' }
|
||||
];
|
||||
|
|
@ -122,6 +123,8 @@ for (const item of contextMenuItems) {
|
|||
browser.contextMenus.create({
|
||||
title: item.title,
|
||||
contexts: menuContexts,
|
||||
visible: item.visible,
|
||||
id: item.id,
|
||||
onclick: (info, tab) => {
|
||||
browser.tabs.sendMessage(tab.id, {
|
||||
action: item.action
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ const defaultSettings = {
|
|||
};
|
||||
|
||||
var page = {};
|
||||
page.attributeMenuItemIds = [];
|
||||
page.blockedTabs = [];
|
||||
page.currentRequest = {};
|
||||
page.currentTabId = -1;
|
||||
|
|
@ -170,7 +171,14 @@ page.initSitePreferences = async function() {
|
|||
await browser.storage.local.set({ 'settings': page.settings });
|
||||
};
|
||||
|
||||
page.switchTab = function(tab) {
|
||||
page.switchTab = async function(tab) {
|
||||
browser.contextMenus.update('fill_attribute', { visible: false });
|
||||
if (page.tabs[tab.id]
|
||||
&& page.tabs[tab.id].credentials.length > 0
|
||||
&& page.tabs[tab.id].credentials.some(e => e.stringFields && e.stringFields.length > 0)) {
|
||||
await page.updateContextMenu(tab, page.tabs[tab.id].credentials);
|
||||
}
|
||||
|
||||
browserAction.showDefault(tab);
|
||||
browser.tabs.sendMessage(tab.id, { action: 'activated_tab' }).catch((e) => {
|
||||
console.log('Cannot send activated_tab message: ', e);
|
||||
|
|
@ -203,6 +211,8 @@ page.clearLogins = function(tabId) {
|
|||
page.tabs[tabId].loginList = [];
|
||||
page.currentRequest = {};
|
||||
page.passwordFilled = false;
|
||||
|
||||
browser.contextMenus.update('fill_attribute', { visible: false });
|
||||
};
|
||||
|
||||
// Clear all logins from all pages and update the content scripts
|
||||
|
|
@ -234,6 +244,7 @@ page.createTabEntry = function(tabId) {
|
|||
};
|
||||
|
||||
page.clearSubmittedCredentials();
|
||||
browser.contextMenus.update('fill_attribute', { visible: false });
|
||||
};
|
||||
|
||||
page.removePageInformationFromNotExistingTabs = async function() {
|
||||
|
|
@ -312,3 +323,51 @@ page.setSubmitted = async function(tab, args = []) {
|
|||
const [ submitted, username, password, url, oldCredentials ] = args;
|
||||
page.setSubmittedCredentials(submitted, username, password, url, oldCredentials, tab.id);
|
||||
};
|
||||
|
||||
// Update context menu for attribute filling
|
||||
page.updateContextMenu = async function(tab, credentials) {
|
||||
// Remove any old attribute items
|
||||
while (page.attributeMenuItemIds.length) {
|
||||
browser.contextMenus.remove(page.attributeMenuItemIds.pop());
|
||||
}
|
||||
|
||||
// Set parent item visibility
|
||||
browser.contextMenus.update('fill_attribute', { visible: true });
|
||||
|
||||
// Add any new attribute items
|
||||
for (const cred of credentials) {
|
||||
if (!cred.stringFields) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const attribute of cred.stringFields) {
|
||||
// Show username inside [] if there are KPH attributes inside multiple credentials
|
||||
const attributeName = Object.keys(attribute)[0].slice(5);
|
||||
const finalName = credentials.length > 1
|
||||
? `[${cred.login}] ${attributeName}`
|
||||
: attributeName;
|
||||
|
||||
page.attributeMenuItemIds.push(createContextMenuItem({
|
||||
action: 'fill_attribute',
|
||||
args: attribute,
|
||||
parentId: 'fill_attribute',
|
||||
title: finalName
|
||||
}));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const createContextMenuItem = function({action, args, ...options}) {
|
||||
return browser.contextMenus.create({
|
||||
contexts: menuContexts,
|
||||
onclick: (info, tab) => {
|
||||
browser.tabs.sendMessage(tab.id, {
|
||||
action: action,
|
||||
args: args
|
||||
}).catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
},
|
||||
...options
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -720,6 +720,21 @@ kpxc.detectDatabaseChange = async function(response) {
|
|||
}
|
||||
};
|
||||
|
||||
// Fill selected attribute from the context menu
|
||||
kpxc.fillAttributeToActiveElementWith = async function(attr) {
|
||||
const el = document.activeElement;
|
||||
if (el.nodeName !== 'INPUT' || kpxc.credentials.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const value = Object.values(attr);
|
||||
if (!value || value.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
kpxc.setValue(el, value[0]);
|
||||
};
|
||||
|
||||
// Fill requested from the context menu. Active element is used for combination detection
|
||||
kpxc.fillInFromActiveElement = async function(passOnly = false) {
|
||||
if (kpxc.credentials.length === 0) {
|
||||
|
|
@ -1292,6 +1307,12 @@ kpxc.retrieveCredentialsCallback = async function(credentials) {
|
|||
await kpxc.prepareCredentials();
|
||||
}
|
||||
|
||||
// Update fill_attribute context menu if String Fields are available
|
||||
const stringFieldsFound = credentials.some(e => e.stringFields && e.stringFields.length > 0);
|
||||
if (stringFieldsFound) {
|
||||
await sendMessage('update_context_menu', credentials);
|
||||
}
|
||||
|
||||
// Retrieve submitted credentials if available
|
||||
const creds = await sendMessage('page_get_submitted');
|
||||
if (creds && creds.submitted) {
|
||||
|
|
@ -1761,6 +1782,9 @@ browser.runtime.onMessage.addListener(async function(req, sender) {
|
|||
} else if (req.action === 'fill_totp') {
|
||||
await kpxc.receiveCredentialsIfNecessary();
|
||||
kpxc.fillFromTOTP();
|
||||
} else if (req.action === 'fill_attribute' && req.args) {
|
||||
await kpxc.receiveCredentialsIfNecessary();
|
||||
kpxc.fillAttributeToActiveElementWith(req.args);
|
||||
} else if (req.action === 'ignore_site') {
|
||||
kpxc.ignoreSite(req.args);
|
||||
} else if (req.action === 'redetect_fields') {
|
||||
|
|
|
|||
Loading…
Reference in a new issue