mirror of
https://github.com/bitwarden/browser.git
synced 2024-12-21 16:18:28 +01:00
Add new context menu item: Copy Custom Field Name (#2045)
* Add "Copy custom field name" context menu item * Title case context menu string * Improve Copy Custom Field Name logic * Move CopyClickedElement to runtime.background * Update dependencies * Add comments, refactor logic, add failure messages * Fix typo and linting * Fix typos * Move null check inside function
This commit is contained in:
parent
ca03703f9d
commit
0bd22dcddc
@ -88,6 +88,9 @@
|
|||||||
"generatePasswordCopied": {
|
"generatePasswordCopied": {
|
||||||
"message": "Generate Password (copied)"
|
"message": "Generate Password (copied)"
|
||||||
},
|
},
|
||||||
|
"copyElementIdentifier": {
|
||||||
|
"message": "Copy Custom Field Name"
|
||||||
|
},
|
||||||
"noMatchingLogins": {
|
"noMatchingLogins": {
|
||||||
"message": "No matching logins."
|
"message": "No matching logins."
|
||||||
},
|
},
|
||||||
|
@ -29,6 +29,8 @@ export default class ContextMenusBackground {
|
|||||||
this.contextMenus.onClicked.addListener(async (info: any, tab: any) => {
|
this.contextMenus.onClicked.addListener(async (info: any, tab: any) => {
|
||||||
if (info.menuItemId === 'generate-password') {
|
if (info.menuItemId === 'generate-password') {
|
||||||
await this.generatePasswordToClipboard();
|
await this.generatePasswordToClipboard();
|
||||||
|
} else if (info.menuItemId === 'copy-identifier') {
|
||||||
|
await this.getClickedElement();
|
||||||
} else if (info.parentMenuItemId === 'autofill' ||
|
} else if (info.parentMenuItemId === 'autofill' ||
|
||||||
info.parentMenuItemId === 'copy-username' ||
|
info.parentMenuItemId === 'copy-username' ||
|
||||||
info.parentMenuItemId === 'copy-password' ||
|
info.parentMenuItemId === 'copy-password' ||
|
||||||
@ -45,6 +47,15 @@ export default class ContextMenusBackground {
|
|||||||
this.passwordGenerationService.addHistory(password);
|
this.passwordGenerationService.addHistory(password);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async getClickedElement() {
|
||||||
|
const tab = await BrowserApi.getTabFromCurrentWindow();
|
||||||
|
if (tab == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BrowserApi.tabSendMessageData(tab, 'getClickedElement');
|
||||||
|
}
|
||||||
|
|
||||||
private async cipherAction(info: any) {
|
private async cipherAction(info: any) {
|
||||||
const id = info.menuItemId.split('_')[1];
|
const id = info.menuItemId.split('_')[1];
|
||||||
if (id === 'noop') {
|
if (id === 'noop') {
|
||||||
|
@ -512,6 +512,14 @@ export default class MainBackground {
|
|||||||
title: this.i18nService.t('generatePasswordCopied'),
|
title: this.i18nService.t('generatePasswordCopied'),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await this.contextMenusCreate({
|
||||||
|
type: 'normal',
|
||||||
|
id: 'copy-identifier',
|
||||||
|
parentId: 'root',
|
||||||
|
contexts: ['all'],
|
||||||
|
title: this.i18nService.t('copyElementIdentifier'),
|
||||||
|
});
|
||||||
|
|
||||||
this.buildingContextMenu = false;
|
this.buildingContextMenu = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,6 +195,8 @@ export default class RuntimeBackground {
|
|||||||
type: 'info',
|
type: 'info',
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
case 'getClickedElementResponse':
|
||||||
|
this.platformUtilsService.copyToClipboard(msg.identifier, { window: window });
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
45
src/content/contextMenuHandler.ts
Normal file
45
src/content/contextMenuHandler.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
const inputTags = ['input', 'textarea', 'select'];
|
||||||
|
const attributes = ['id', 'name', 'label-aria', 'placeholder'];
|
||||||
|
let clickedEl: HTMLElement = null;
|
||||||
|
|
||||||
|
// Find the best attribute to be used as the Name for an element in a custom field.
|
||||||
|
function getClickedElementIdentifier() {
|
||||||
|
if (clickedEl == null) {
|
||||||
|
return 'Unable to identify clicked element.'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!inputTags.includes(clickedEl.nodeName.toLowerCase())) {
|
||||||
|
return 'Invalid element type.';
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const attr of attributes) {
|
||||||
|
const attributeValue = clickedEl.getAttribute(attr);
|
||||||
|
const selector = '[' + attr + '="' + attributeValue + '"]';
|
||||||
|
if (!isNullOrEmpty(attributeValue) && document.querySelectorAll(selector)?.length === 1) {
|
||||||
|
return attributeValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 'No unique identifier found.';
|
||||||
|
}
|
||||||
|
|
||||||
|
function isNullOrEmpty(s: string) {
|
||||||
|
return s == null || s === '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// We only have access to the element that's been clicked when the context menu is first opened.
|
||||||
|
// Remember it for use later.
|
||||||
|
document.addEventListener('contextmenu', event => {
|
||||||
|
clickedEl = event.target as HTMLElement;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Runs when the 'Copy Custom Field Name' context menu item is actually clicked.
|
||||||
|
chrome.runtime.onMessage.addListener(event => {
|
||||||
|
if (event.command === 'getClickedElement') {
|
||||||
|
const identifier = getClickedElementIdentifier();
|
||||||
|
chrome.runtime.sendMessage({
|
||||||
|
command: 'getClickedElementResponse',
|
||||||
|
sender: 'contextMenuHandler',
|
||||||
|
identifier: identifier,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
@ -20,7 +20,8 @@
|
|||||||
"js": [
|
"js": [
|
||||||
"content/autofill.js",
|
"content/autofill.js",
|
||||||
"content/autofiller.js",
|
"content/autofiller.js",
|
||||||
"content/notificationBar.js"
|
"content/notificationBar.js",
|
||||||
|
"content/contextMenuHandler.js"
|
||||||
],
|
],
|
||||||
"matches": [
|
"matches": [
|
||||||
"http://*/*",
|
"http://*/*",
|
||||||
|
@ -130,6 +130,7 @@ const config = {
|
|||||||
'content/autofill': './src/content/autofill.js',
|
'content/autofill': './src/content/autofill.js',
|
||||||
'content/autofiller': './src/content/autofiller.ts',
|
'content/autofiller': './src/content/autofiller.ts',
|
||||||
'content/notificationBar': './src/content/notificationBar.ts',
|
'content/notificationBar': './src/content/notificationBar.ts',
|
||||||
|
'content/contextMenuHandler': './src/content/contextMenuHandler.ts',
|
||||||
'content/shortcuts': './src/content/shortcuts.ts',
|
'content/shortcuts': './src/content/shortcuts.ts',
|
||||||
'content/message_handler': './src/content/message_handler.ts',
|
'content/message_handler': './src/content/message_handler.ts',
|
||||||
'notification/bar': './src/notification/bar.js',
|
'notification/bar': './src/notification/bar.js',
|
||||||
|
Loading…
Reference in New Issue
Block a user