From 9a9a973ef0acc267efa0c47a68882ca2e50616f9 Mon Sep 17 00:00:00 2001 From: Cesar Gonzalez Date: Thu, 11 Apr 2024 09:53:04 -0500 Subject: [PATCH] [PM-5189] Extracting the getAttributeBoolean method to a separate util method --- .../autofill-overlay-content.service.ts | 4 +- .../collect-autofill-content.service.ts | 46 +------- apps/browser/src/autofill/utils/index.ts | 101 ++++++++++-------- 3 files changed, 65 insertions(+), 86 deletions(-) diff --git a/apps/browser/src/autofill/services/autofill-overlay-content.service.ts b/apps/browser/src/autofill/services/autofill-overlay-content.service.ts index 04ae7554112..9fd192d95ab 100644 --- a/apps/browser/src/autofill/services/autofill-overlay-content.service.ts +++ b/apps/browser/src/autofill/services/autofill-overlay-content.service.ts @@ -12,7 +12,7 @@ import { import { AutofillExtensionMessage } from "../content/abstractions/autofill-init"; import AutofillField from "../models/autofill-field"; import { ElementWithOpId, FillableFormFieldElement, FormFieldElement } from "../types"; -import { elementIsFillableFormField, sendExtensionMessage } from "../utils"; +import { elementIsFillableFormField, getAttributeBoolean, sendExtensionMessage } from "../utils"; import { AutofillOverlayElement, RedirectFocusDirection } from "../utils/autofill-overlay.enum"; import { @@ -708,6 +708,8 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte const formFieldElement = event.target as ElementWithOpId; const autofillFieldData = this.hiddenFormFieldElements.get(formFieldElement); if (autofillFieldData) { + autofillFieldData.readonly = getAttributeBoolean(formFieldElement, "disabled"); + autofillFieldData.disabled = getAttributeBoolean(formFieldElement, "disabled"); autofillFieldData.viewable = true; void this.setupAutofillOverlayListenerOnField(formFieldElement, autofillFieldData); } diff --git a/apps/browser/src/autofill/services/collect-autofill-content.service.ts b/apps/browser/src/autofill/services/collect-autofill-content.service.ts index 7e418232aea..bc5df6f5806 100644 --- a/apps/browser/src/autofill/services/collect-autofill-content.service.ts +++ b/apps/browser/src/autofill/services/collect-autofill-content.service.ts @@ -1,12 +1,7 @@ import AutofillField from "../models/autofill-field"; import AutofillForm from "../models/autofill-form"; import AutofillPageDetails from "../models/autofill-page-details"; -import { - ElementWithOpId, - FillableFormFieldElement, - FormFieldElement, - FormElementWithAttribute, -} from "../types"; +import { ElementWithOpId, FillableFormFieldElement, FormFieldElement } from "../types"; import { elementIsDescriptionDetailsElement, elementIsDescriptionTermElement, @@ -19,6 +14,8 @@ import { nodeIsElement, elementIsInputElement, elementIsTextAreaElement, + getAttributeBoolean, + getPropertyOrAttribute, } from "../utils"; import { AutofillOverlayContentService } from "./abstractions/autofill-overlay-content.service"; @@ -33,6 +30,8 @@ import { DomElementVisibilityService } from "./abstractions/dom-element-visibili class CollectAutofillContentService implements CollectAutofillContentServiceInterface { private readonly domElementVisibilityService: DomElementVisibilityService; private readonly autofillOverlayContentService: AutofillOverlayContentService; + private readonly getAttributeBoolean = getAttributeBoolean; + private readonly getPropertyOrAttribute = getPropertyOrAttribute; private noFieldsFound = false; private domRecentlyMutated = true; private autofillFormElements: AutofillFormElements = new Map(); @@ -467,26 +466,6 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte return autoCompleteType !== "off" ? autoCompleteType : null; } - /** - * Returns a boolean representing the attribute value of an element. - * @param {ElementWithOpId} element - * @param {string} attributeName - * @param {boolean} checkString - * @returns {boolean} - * @private - */ - private getAttributeBoolean( - element: ElementWithOpId, - attributeName: string, - checkString = false, - ): boolean { - if (checkString) { - return this.getPropertyOrAttribute(element, attributeName) === "true"; - } - - return Boolean(this.getPropertyOrAttribute(element, attributeName)); - } - /** * Returns the attribute of an element as a lowercase value. * @param {ElementWithOpId} element @@ -798,21 +777,6 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte return this.recursivelyGetTextFromPreviousSiblings(siblingElement); } - /** - * Get the value of a property or attribute from a FormFieldElement. - * @param {HTMLElement} element - * @param {string} attributeName - * @returns {string | null} - * @private - */ - private getPropertyOrAttribute(element: HTMLElement, attributeName: string): string | null { - if (attributeName in element) { - return (element as FormElementWithAttribute)[attributeName]; - } - - return element.getAttribute(attributeName); - } - /** * Gets the value of the element. If the element is a checkbox, returns a checkmark if the * checkbox is checked, or an empty string if it is not checked. If the element is a hidden diff --git a/apps/browser/src/autofill/utils/index.ts b/apps/browser/src/autofill/utils/index.ts index 8a51aa10378..a890dd20335 100644 --- a/apps/browser/src/autofill/utils/index.ts +++ b/apps/browser/src/autofill/utils/index.ts @@ -1,7 +1,7 @@ import { AutofillPort } from "../enums/autofill-port.enums"; -import { FillableFormFieldElement, FormFieldElement } from "../types"; +import { FillableFormFieldElement, FormElementWithAttribute, FormFieldElement } from "../types"; -function generateRandomChars(length: number): string { +export function generateRandomChars(length: number): string { const chars = "abcdefghijklmnopqrstuvwxyz"; const randomChars = []; const randomBytes = new Uint8Array(length); @@ -18,7 +18,7 @@ function generateRandomChars(length: number): string { /** * Generates a random string of characters that formatted as a custom element name. */ -function generateRandomCustomElementName(): string { +export function generateRandomCustomElementName(): string { const length = Math.floor(Math.random() * 5) + 8; // Between 8 and 12 characters const numHyphens = Math.min(Math.max(Math.floor(Math.random() * 4), 1), length - 1); // At least 1, maximum of 3 hyphens @@ -51,7 +51,7 @@ function generateRandomCustomElementName(): string { * @param svgString - The SVG string to build the DOM element from. * @param ariaHidden - Determines whether the SVG should be hidden from screen readers. */ -function buildSvgDomElement(svgString: string, ariaHidden = true): HTMLElement { +export function buildSvgDomElement(svgString: string, ariaHidden = true): HTMLElement { const domParser = new DOMParser(); const svgDom = domParser.parseFromString(svgString, "image/svg+xml"); const domElement = svgDom.documentElement; @@ -66,7 +66,7 @@ function buildSvgDomElement(svgString: string, ariaHidden = true): HTMLElement { * @param command - The command to send. * @param options - The options to send with the command. */ -async function sendExtensionMessage( +export async function sendExtensionMessage( command: string, options: Record = {}, ): Promise { @@ -88,7 +88,7 @@ async function sendExtensionMessage( * @param styles - The styles to set on the element. * @param priority - Determines whether the styles should be set as important. */ -function setElementStyles( +export function setElementStyles( element: HTMLElement, styles: Partial, priority?: boolean, @@ -111,9 +111,9 @@ function setElementStyles( * and triggers an onDisconnect event if the extension context * is invalidated. * - * @param callback - Callback function to run when the extension disconnects + * @param callback - Callback export function to run when the extension disconnects */ -function setupExtensionDisconnectAction(callback: (port: chrome.runtime.Port) => void) { +export function setupExtensionDisconnectAction(callback: (port: chrome.runtime.Port) => void) { const port = chrome.runtime.connect({ name: AutofillPort.InjectedScript }); const onDisconnectCallback = (disconnectedPort: chrome.runtime.Port) => { callback(disconnectedPort); @@ -128,7 +128,7 @@ function setupExtensionDisconnectAction(callback: (port: chrome.runtime.Port) => * * @param windowContext - The global window context */ -function setupAutofillInitDisconnectAction(windowContext: Window) { +export function setupAutofillInitDisconnectAction(windowContext: Window) { if (!windowContext.bitwardenAutofillInit) { return; } @@ -146,7 +146,7 @@ function setupAutofillInitDisconnectAction(windowContext: Window) { * * @param formFieldElement - The form field element to check. */ -function elementIsFillableFormField( +export function elementIsFillableFormField( formFieldElement: FormFieldElement, ): formFieldElement is FillableFormFieldElement { return !elementIsSpanElement(formFieldElement); @@ -158,7 +158,10 @@ function elementIsFillableFormField( * @param element - The element to check. * @param tagName - The tag name to check against. */ -function elementIsInstanceOf(element: Element, tagName: string): element is T { +export function elementIsInstanceOf( + element: Element, + tagName: string, +): element is T { return nodeIsElement(element) && element.tagName.toLowerCase() === tagName; } @@ -167,7 +170,7 @@ function elementIsInstanceOf(element: Element, tagName: strin * * @param element - The element to check. */ -function elementIsSpanElement(element: Element): element is HTMLSpanElement { +export function elementIsSpanElement(element: Element): element is HTMLSpanElement { return elementIsInstanceOf(element, "span"); } @@ -176,7 +179,7 @@ function elementIsSpanElement(element: Element): element is HTMLSpanElement { * * @param element - The element to check. */ -function elementIsInputElement(element: Element): element is HTMLInputElement { +export function elementIsInputElement(element: Element): element is HTMLInputElement { return elementIsInstanceOf(element, "input"); } @@ -185,7 +188,7 @@ function elementIsInputElement(element: Element): element is HTMLInputElement { * * @param element - The element to check. */ -function elementIsSelectElement(element: Element): element is HTMLSelectElement { +export function elementIsSelectElement(element: Element): element is HTMLSelectElement { return elementIsInstanceOf(element, "select"); } @@ -194,7 +197,7 @@ function elementIsSelectElement(element: Element): element is HTMLSelectElement * * @param element - The element to check. */ -function elementIsTextAreaElement(element: Element): element is HTMLTextAreaElement { +export function elementIsTextAreaElement(element: Element): element is HTMLTextAreaElement { return elementIsInstanceOf(element, "textarea"); } @@ -203,7 +206,7 @@ function elementIsTextAreaElement(element: Element): element is HTMLTextAreaElem * * @param element - The element to check. */ -function elementIsFormElement(element: Element): element is HTMLFormElement { +export function elementIsFormElement(element: Element): element is HTMLFormElement { return elementIsInstanceOf(element, "form"); } @@ -212,7 +215,7 @@ function elementIsFormElement(element: Element): element is HTMLFormElement { * * @param element - The element to check. */ -function elementIsLabelElement(element: Element): element is HTMLLabelElement { +export function elementIsLabelElement(element: Element): element is HTMLLabelElement { return elementIsInstanceOf(element, "label"); } @@ -221,7 +224,7 @@ function elementIsLabelElement(element: Element): element is HTMLLabelElement { * * @param element - The element to check. */ -function elementIsDescriptionDetailsElement(element: Element): element is HTMLElement { +export function elementIsDescriptionDetailsElement(element: Element): element is HTMLElement { return elementIsInstanceOf(element, "dd"); } @@ -230,7 +233,7 @@ function elementIsDescriptionDetailsElement(element: Element): element is HTMLEl * * @param element - The element to check. */ -function elementIsDescriptionTermElement(element: Element): element is HTMLElement { +export function elementIsDescriptionTermElement(element: Element): element is HTMLElement { return elementIsInstanceOf(element, "dt"); } @@ -239,7 +242,7 @@ function elementIsDescriptionTermElement(element: Element): element is HTMLEleme * * @param node - The node to check. */ -function nodeIsElement(node: Node): node is Element { +export function nodeIsElement(node: Node): node is Element { return node?.nodeType === Node.ELEMENT_NODE; } @@ -248,7 +251,7 @@ function nodeIsElement(node: Node): node is Element { * * @param node - The node to check. */ -function nodeIsInputElement(node: Node): node is HTMLInputElement { +export function nodeIsInputElement(node: Node): node is HTMLInputElement { return nodeIsElement(node) && elementIsInputElement(node); } @@ -257,29 +260,39 @@ function nodeIsInputElement(node: Node): node is HTMLInputElement { * * @param node - The node to check. */ -function nodeIsFormElement(node: Node): node is HTMLFormElement { +export function nodeIsFormElement(node: Node): node is HTMLFormElement { return nodeIsElement(node) && elementIsFormElement(node); } -export { - generateRandomChars, - generateRandomCustomElementName, - buildSvgDomElement, - sendExtensionMessage, - setElementStyles, - setupExtensionDisconnectAction, - setupAutofillInitDisconnectAction, - elementIsFillableFormField, - elementIsInstanceOf, - elementIsSpanElement, - elementIsInputElement, - elementIsSelectElement, - elementIsTextAreaElement, - elementIsFormElement, - elementIsLabelElement, - elementIsDescriptionDetailsElement, - elementIsDescriptionTermElement, - nodeIsElement, - nodeIsInputElement, - nodeIsFormElement, -}; +/** + * Returns a boolean representing the attribute value of an element. + * + * @param element + * @param attributeName + * @param checkString + */ +export function getAttributeBoolean( + element: HTMLElement, + attributeName: string, + checkString = false, +): boolean { + if (checkString) { + return getPropertyOrAttribute(element, attributeName) === "true"; + } + + return Boolean(getPropertyOrAttribute(element, attributeName)); +} + +/** + * Get the value of a property or attribute from a FormFieldElement. + * + * @param element + * @param attributeName + */ +export function getPropertyOrAttribute(element: HTMLElement, attributeName: string): string | null { + if (attributeName in element) { + return (element as FormElementWithAttribute)[attributeName]; + } + + return element.getAttribute(attributeName); +}