mirror of
https://github.com/bitwarden/browser
synced 2025-12-13 14:53:33 +00:00
[PM-4923] Form elements that fade into view contain incorrectly cached page details (#6953)
* [PM-4923] Form Elements that Fade into View Contain Incorrectly Cached Page Details * [PM-4923] Form Elements that Fade into View Contain Incorrectly Cached Page Details * [PM-4923] Running prettier on implementation
This commit is contained in:
@@ -2051,6 +2051,83 @@ describe("CollectAutofillContentService", () => {
|
|||||||
collectAutofillContentService["handleAutofillElementAttributeMutation"],
|
collectAutofillContentService["handleAutofillElementAttributeMutation"],
|
||||||
).not.toBeCalled();
|
).not.toBeCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("will setup the overlay listeners on mutated elements", async () => {
|
||||||
|
jest.useFakeTimers();
|
||||||
|
const form = document.createElement("form");
|
||||||
|
document.body.appendChild(form);
|
||||||
|
const addedNodes = document.querySelectorAll("form");
|
||||||
|
const removedNodes = document.querySelectorAll("li");
|
||||||
|
const mutationRecord: MutationRecord = {
|
||||||
|
type: "childList",
|
||||||
|
addedNodes: addedNodes,
|
||||||
|
attributeName: null,
|
||||||
|
attributeNamespace: null,
|
||||||
|
nextSibling: null,
|
||||||
|
oldValue: null,
|
||||||
|
previousSibling: null,
|
||||||
|
removedNodes: removedNodes,
|
||||||
|
target: document.body,
|
||||||
|
};
|
||||||
|
collectAutofillContentService["domRecentlyMutated"] = false;
|
||||||
|
collectAutofillContentService["noFieldsFound"] = true;
|
||||||
|
collectAutofillContentService["currentLocationHref"] = window.location.href;
|
||||||
|
jest.spyOn(collectAutofillContentService as any, "setupOverlayListenersOnMutatedElements");
|
||||||
|
|
||||||
|
collectAutofillContentService["handleMutationObserverMutation"]([mutationRecord]);
|
||||||
|
jest.runAllTimers();
|
||||||
|
|
||||||
|
expect(collectAutofillContentService["setupOverlayListenersOnMutatedElements"]).toBeCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("setupOverlayListenersOnMutatedElements", () => {
|
||||||
|
it("skips building the autofill field item if the node is not a form field element", () => {
|
||||||
|
const divElement = document.createElement("div");
|
||||||
|
const nodes = [divElement];
|
||||||
|
jest.spyOn(collectAutofillContentService as any, "buildAutofillFieldItem");
|
||||||
|
|
||||||
|
collectAutofillContentService["setupOverlayListenersOnMutatedElements"](nodes);
|
||||||
|
|
||||||
|
expect(collectAutofillContentService["buildAutofillFieldItem"]).not.toBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("skips building the autofill field item if the node is already a field element", () => {
|
||||||
|
const inputElement = document.createElement("input") as ElementWithOpId<HTMLInputElement>;
|
||||||
|
inputElement.setAttribute("type", "password");
|
||||||
|
const nodes = [inputElement];
|
||||||
|
collectAutofillContentService["autofillFieldElements"].set(inputElement, {
|
||||||
|
opid: "1234",
|
||||||
|
} as AutofillField);
|
||||||
|
jest.spyOn(collectAutofillContentService as any, "buildAutofillFieldItem");
|
||||||
|
|
||||||
|
collectAutofillContentService["setupOverlayListenersOnMutatedElements"](nodes);
|
||||||
|
|
||||||
|
expect(collectAutofillContentService["buildAutofillFieldItem"]).not.toBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("builds the autofill field item to ensure the overlay listeners are set", () => {
|
||||||
|
document.body.innerHTML = `
|
||||||
|
<form>
|
||||||
|
<label for="username-id">Username Label</label>
|
||||||
|
<input type="text" id="username-id">
|
||||||
|
</form>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const inputElement = document.getElementById(
|
||||||
|
"username-id",
|
||||||
|
) as ElementWithOpId<HTMLInputElement>;
|
||||||
|
inputElement.setAttribute("type", "password");
|
||||||
|
const nodes = [inputElement];
|
||||||
|
jest.spyOn(collectAutofillContentService as any, "buildAutofillFieldItem");
|
||||||
|
|
||||||
|
collectAutofillContentService["setupOverlayListenersOnMutatedElements"](nodes);
|
||||||
|
|
||||||
|
expect(collectAutofillContentService["buildAutofillFieldItem"]).toBeCalledWith(
|
||||||
|
inputElement,
|
||||||
|
-1,
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("deleteCachedAutofillElement", () => {
|
describe("deleteCachedAutofillElement", () => {
|
||||||
|
|||||||
@@ -303,7 +303,7 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
|
|||||||
element.opid = `__${index}`;
|
element.opid = `__${index}`;
|
||||||
|
|
||||||
const existingAutofillField = this.autofillFieldElements.get(element);
|
const existingAutofillField = this.autofillFieldElements.get(element);
|
||||||
if (existingAutofillField) {
|
if (index >= 0 && existingAutofillField) {
|
||||||
existingAutofillField.opid = element.opid;
|
existingAutofillField.opid = element.opid;
|
||||||
existingAutofillField.elementNumber = index;
|
existingAutofillField.elementNumber = index;
|
||||||
this.autofillFieldElements.set(element, existingAutofillField);
|
this.autofillFieldElements.set(element, existingAutofillField);
|
||||||
@@ -325,7 +325,7 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (element instanceof HTMLSpanElement) {
|
if (element instanceof HTMLSpanElement) {
|
||||||
this.autofillFieldElements.set(element, autofillFieldBase);
|
this.cacheAutofillFieldElement(index, element, autofillFieldBase);
|
||||||
this.autofillOverlayContentService?.setupAutofillOverlayListenerOnField(
|
this.autofillOverlayContentService?.setupAutofillOverlayListenerOnField(
|
||||||
element,
|
element,
|
||||||
autofillFieldBase,
|
autofillFieldBase,
|
||||||
@@ -366,11 +366,31 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
|
|||||||
"data-stripe": this.getPropertyOrAttribute(element, "data-stripe"),
|
"data-stripe": this.getPropertyOrAttribute(element, "data-stripe"),
|
||||||
};
|
};
|
||||||
|
|
||||||
this.autofillFieldElements.set(element, autofillField);
|
this.cacheAutofillFieldElement(index, element, autofillField);
|
||||||
this.autofillOverlayContentService?.setupAutofillOverlayListenerOnField(element, autofillField);
|
this.autofillOverlayContentService?.setupAutofillOverlayListenerOnField(element, autofillField);
|
||||||
return autofillField;
|
return autofillField;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Caches the autofill field element and its data.
|
||||||
|
* Will not cache the element if the index is less than 0.
|
||||||
|
*
|
||||||
|
* @param index - The index of the autofill field element
|
||||||
|
* @param element - The autofill field element to cache
|
||||||
|
* @param autofillFieldData - The autofill field data to cache
|
||||||
|
*/
|
||||||
|
private cacheAutofillFieldElement(
|
||||||
|
index: number,
|
||||||
|
element: ElementWithOpId<FormFieldElement>,
|
||||||
|
autofillFieldData: AutofillField,
|
||||||
|
) {
|
||||||
|
if (index < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.autofillFieldElements.set(element, autofillFieldData);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Identifies the autocomplete attribute associated with an element and returns
|
* Identifies the autocomplete attribute associated with an element and returns
|
||||||
* the value of the attribute if it is not set to "off".
|
* the value of the attribute if it is not set to "off".
|
||||||
@@ -987,7 +1007,7 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
|
|||||||
}
|
}
|
||||||
|
|
||||||
let isElementMutated = false;
|
let isElementMutated = false;
|
||||||
const mutatedElements = [];
|
const mutatedElements: Node[] = [];
|
||||||
for (let index = 0; index < nodes.length; index++) {
|
for (let index = 0; index < nodes.length; index++) {
|
||||||
const node = nodes[index];
|
const node = nodes[index];
|
||||||
if (!(node instanceof HTMLElement)) {
|
if (!(node instanceof HTMLElement)) {
|
||||||
@@ -1010,17 +1030,31 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let elementIndex = 0; elementIndex < mutatedElements.length; elementIndex++) {
|
if (isRemovingNodes) {
|
||||||
const node = mutatedElements[elementIndex];
|
for (let elementIndex = 0; elementIndex < mutatedElements.length; elementIndex++) {
|
||||||
if (isRemovingNodes) {
|
const node = mutatedElements[elementIndex];
|
||||||
this.deleteCachedAutofillElement(
|
this.deleteCachedAutofillElement(
|
||||||
node as ElementWithOpId<HTMLFormElement> | ElementWithOpId<FormFieldElement>,
|
node as ElementWithOpId<HTMLFormElement> | ElementWithOpId<FormFieldElement>,
|
||||||
);
|
);
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
} else if (this.autofillOverlayContentService) {
|
||||||
|
setTimeout(() => this.setupOverlayListenersOnMutatedElements(mutatedElements), 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
return isElementMutated;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up the overlay listeners on the passed mutated elements. This ensures
|
||||||
|
* that the overlay can appear on elements that are injected into the DOM after
|
||||||
|
* the initial page load.
|
||||||
|
*
|
||||||
|
* @param mutatedElements - HTML elements that have been mutated
|
||||||
|
*/
|
||||||
|
private setupOverlayListenersOnMutatedElements(mutatedElements: Node[]) {
|
||||||
|
for (let elementIndex = 0; elementIndex < mutatedElements.length; elementIndex++) {
|
||||||
|
const node = mutatedElements[elementIndex];
|
||||||
if (
|
if (
|
||||||
this.autofillOverlayContentService &&
|
|
||||||
this.isNodeFormFieldElement(node) &&
|
this.isNodeFormFieldElement(node) &&
|
||||||
!this.autofillFieldElements.get(node as ElementWithOpId<FormFieldElement>)
|
!this.autofillFieldElements.get(node as ElementWithOpId<FormFieldElement>)
|
||||||
) {
|
) {
|
||||||
@@ -1029,8 +1063,6 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
|
|||||||
this.buildAutofillFieldItem(node as ElementWithOpId<FormFieldElement>, -1);
|
this.buildAutofillFieldItem(node as ElementWithOpId<FormFieldElement>, -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return isElementMutated;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user