1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-09-28 04:08:47 +02: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:
Thomas Rittson 2021-10-07 09:52:33 +10:00 committed by GitHub
parent bbcbcf2b40
commit cffd4b3515
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 40 additions and 9 deletions

View File

@ -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."
} }
} }

View File

@ -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) {

View File

@ -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) {

View File

@ -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) {

View File

@ -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',