mirror of
https://github.com/bitwarden/browser.git
synced 2025-01-03 18:28:13 +01:00
Fix Copy Custom Field Name on pages with iframes (#2091)
* Improve error messages * Send getClickedElement msg to specific frameId * Add support for finding input element from label * Use i18n for error messages * Fix unrelated linting
This commit is contained in:
parent
bbcbcf2b40
commit
cffd4b3515
@ -1807,5 +1807,11 @@
|
|||||||
},
|
},
|
||||||
"personalVaultExportPolicyInEffect": {
|
"personalVaultExportPolicyInEffect": {
|
||||||
"message": "One or more organization policies prevents you from exporting your personal vault."
|
"message": "One or more organization policies prevents you from exporting your personal vault."
|
||||||
|
},
|
||||||
|
"copyCustomFieldNameInvalidElement": {
|
||||||
|
"message": "Unable to identify a valid form element. Try inspecting the HTML instead."
|
||||||
|
},
|
||||||
|
"copyCustomFieldNameNotUnique": {
|
||||||
|
"message": "No unique identifier found."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ export default class ContextMenusBackground {
|
|||||||
if (info.menuItemId === 'generate-password') {
|
if (info.menuItemId === 'generate-password') {
|
||||||
await this.generatePasswordToClipboard();
|
await this.generatePasswordToClipboard();
|
||||||
} else if (info.menuItemId === 'copy-identifier') {
|
} else if (info.menuItemId === 'copy-identifier') {
|
||||||
await this.getClickedElement();
|
await this.getClickedElement(info.frameId);
|
||||||
} 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' ||
|
||||||
@ -47,13 +47,13 @@ export default class ContextMenusBackground {
|
|||||||
this.passwordGenerationService.addHistory(password);
|
this.passwordGenerationService.addHistory(password);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getClickedElement() {
|
private async getClickedElement(frameId: number) {
|
||||||
const tab = await BrowserApi.getTabFromCurrentWindow();
|
const tab = await BrowserApi.getTabFromCurrentWindow();
|
||||||
if (tab == null) {
|
if (tab == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
BrowserApi.tabSendMessageData(tab, 'getClickedElement');
|
BrowserApi.tabSendMessage(tab, { command: 'getClickedElement' }, { frameId: frameId });
|
||||||
}
|
}
|
||||||
|
|
||||||
private async cipherAction(info: any) {
|
private async cipherAction(info: any) {
|
||||||
|
@ -1,25 +1,49 @@
|
|||||||
const inputTags = ['input', 'textarea', 'select'];
|
const inputTags = ['input', 'textarea', 'select'];
|
||||||
|
const labelTags = ['label', 'span'];
|
||||||
const attributes = ['id', 'name', 'label-aria', 'placeholder'];
|
const attributes = ['id', 'name', 'label-aria', 'placeholder'];
|
||||||
|
const invalidElement = chrome.i18n.getMessage('copyCustomFieldNameInvalidElement');
|
||||||
|
const noUniqueIdentifier = chrome.i18n.getMessage('copyCustomFieldNameNotUnique');
|
||||||
|
|
||||||
let clickedEl: HTMLElement = null;
|
let clickedEl: HTMLElement = null;
|
||||||
|
|
||||||
// Find the best attribute to be used as the Name for an element in a custom field.
|
// Find the best attribute to be used as the Name for an element in a custom field.
|
||||||
function getClickedElementIdentifier() {
|
function getClickedElementIdentifier() {
|
||||||
if (clickedEl == null) {
|
if (clickedEl == null) {
|
||||||
return 'Unable to identify clicked element.';
|
return invalidElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!inputTags.includes(clickedEl.nodeName.toLowerCase())) {
|
const tagName = clickedEl.nodeName.toLowerCase();
|
||||||
return 'Invalid element type.';
|
let inputEl = null;
|
||||||
|
|
||||||
|
// Try to identify the input element (which may not be the clicked element)
|
||||||
|
if (inputTags.includes(tagName)) {
|
||||||
|
inputEl = clickedEl;
|
||||||
|
} else if (labelTags.includes(tagName)) {
|
||||||
|
let inputName = null;
|
||||||
|
if (tagName === 'label') {
|
||||||
|
inputName = clickedEl.getAttribute('for');
|
||||||
|
} else {
|
||||||
|
inputName = clickedEl.closest('label')?.getAttribute('for');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputName != null) {
|
||||||
|
inputEl = document.querySelector('input[name=' + inputName + '], select[name=' + inputName +
|
||||||
|
'], textarea[name=' + inputName + ']');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputEl == null) {
|
||||||
|
return invalidElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const attr of attributes) {
|
for (const attr of attributes) {
|
||||||
const attributeValue = clickedEl.getAttribute(attr);
|
const attributeValue = inputEl.getAttribute(attr);
|
||||||
const selector = '[' + attr + '="' + attributeValue + '"]';
|
const selector = '[' + attr + '="' + attributeValue + '"]';
|
||||||
if (!isNullOrEmpty(attributeValue) && document.querySelectorAll(selector)?.length === 1) {
|
if (!isNullOrEmpty(attributeValue) && document.querySelectorAll(selector)?.length === 1) {
|
||||||
return attributeValue;
|
return attributeValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 'No unique identifier found.';
|
return noUniqueIdentifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isNullOrEmpty(s: string) {
|
function isNullOrEmpty(s: string) {
|
||||||
|
@ -62,6 +62,7 @@ import { StateService } from 'jslib-common/services/state.service';
|
|||||||
|
|
||||||
import { PopupSearchService } from './popup-search.service';
|
import { PopupSearchService } from './popup-search.service';
|
||||||
import { PopupUtilsService } from './popup-utils.service';
|
import { PopupUtilsService } from './popup-utils.service';
|
||||||
|
|
||||||
import { ThemeType } from 'jslib-common/enums/themeType';
|
import { ThemeType } from 'jslib-common/enums/themeType';
|
||||||
|
|
||||||
function getBgService<T>(service: string) {
|
function getBgService<T>(service: string) {
|
||||||
|
@ -3,6 +3,7 @@ import {
|
|||||||
OnInit,
|
OnInit,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
|
||||||
|
import { ThemeType } from 'jslib-common/enums/themeType';
|
||||||
import { UriMatchType } from 'jslib-common/enums/uriMatchType';
|
import { UriMatchType } from 'jslib-common/enums/uriMatchType';
|
||||||
|
|
||||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||||
@ -12,7 +13,6 @@ import { StorageService } from 'jslib-common/abstractions/storage.service';
|
|||||||
import { TotpService } from 'jslib-common/abstractions/totp.service';
|
import { TotpService } from 'jslib-common/abstractions/totp.service';
|
||||||
|
|
||||||
import { ConstantsService } from 'jslib-common/services/constants.service';
|
import { ConstantsService } from 'jslib-common/services/constants.service';
|
||||||
import { ThemeType } from 'jslib-common/enums/themeType';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-options',
|
selector: 'app-options',
|
||||||
|
Loading…
Reference in New Issue
Block a user