mirror of
https://github.com/bitwarden/browser
synced 2026-02-06 03:33:30 +00:00
add teardown of listeners/observers
This commit is contained in:
@@ -72,21 +72,24 @@ class AutofillInit implements AutofillInitInterface {
|
||||
* to act on the page.
|
||||
*/
|
||||
private collectPageDetailsOnLoad() {
|
||||
const sendCollectDetailsMessage = () => {
|
||||
this.clearCollectPageDetailsOnLoadTimeout();
|
||||
this.collectPageDetailsOnLoadTimeout = setTimeout(
|
||||
() => this.sendExtensionMessage("bgCollectPageDetails", { sender: "autofillInit" }),
|
||||
750,
|
||||
);
|
||||
};
|
||||
|
||||
if (globalThis.document.readyState === "complete") {
|
||||
sendCollectDetailsMessage();
|
||||
this.sendCollectDetailsMessage();
|
||||
}
|
||||
|
||||
globalThis.addEventListener(EVENTS.LOAD, sendCollectDetailsMessage);
|
||||
globalThis.addEventListener(EVENTS.LOAD, this.sendCollectDetailsMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to collect page details after a short delay.
|
||||
*/
|
||||
private sendCollectDetailsMessage = () => {
|
||||
this.clearCollectPageDetailsOnLoadTimeout();
|
||||
this.collectPageDetailsOnLoadTimeout = setTimeout(
|
||||
() => this.sendExtensionMessage("bgCollectPageDetails", { sender: "autofillInit" }),
|
||||
750,
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Collects the page details and sends them to the
|
||||
* extension background script. If the `sendDetailsInResponse`
|
||||
@@ -218,6 +221,7 @@ class AutofillInit implements AutofillInitInterface {
|
||||
*/
|
||||
destroy() {
|
||||
this.clearCollectPageDetailsOnLoadTimeout();
|
||||
globalThis.removeEventListener(EVENTS.LOAD, this.sendCollectDetailsMessage);
|
||||
chrome.runtime.onMessage.removeListener(this.handleExtensionMessage);
|
||||
this.collectAutofillContentService.destroy();
|
||||
this.autofillOverlayContentService?.destroy();
|
||||
|
||||
@@ -32,4 +32,5 @@ export type BackgroundPortMessageHandlers = {
|
||||
|
||||
export interface AutofillInlineMenuIframeService {
|
||||
initMenuIframe(): void;
|
||||
destroy(): void;
|
||||
}
|
||||
|
||||
@@ -41,7 +41,9 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte
|
||||
globalThis.navigator.userAgent.indexOf(" Firefox/") !== -1 ||
|
||||
globalThis.navigator.userAgent.indexOf(" Gecko/") !== -1;
|
||||
private buttonElement?: HTMLElement;
|
||||
private buttonIframe?: AutofillInlineMenuButtonIframe;
|
||||
private listElement?: HTMLElement;
|
||||
private listIframe?: AutofillInlineMenuListIframe;
|
||||
private htmlMutationObserver: MutationObserver;
|
||||
private bodyMutationObserver: MutationObserver;
|
||||
private inlineMenuElementsMutationObserver: MutationObserver;
|
||||
@@ -264,18 +266,19 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte
|
||||
if (this.isFirefoxBrowser) {
|
||||
this.buttonElement = globalThis.document.createElement("div");
|
||||
this.buttonElement.setAttribute("popover", "manual");
|
||||
new AutofillInlineMenuButtonIframe(this.buttonElement);
|
||||
this.buttonIframe = new AutofillInlineMenuButtonIframe(this.buttonElement);
|
||||
|
||||
return this.buttonElement;
|
||||
}
|
||||
|
||||
const customElementName = this.generateRandomCustomElementName();
|
||||
const self = this;
|
||||
globalThis.customElements?.define(
|
||||
customElementName,
|
||||
class extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
new AutofillInlineMenuButtonIframe(this);
|
||||
self.buttonIframe = new AutofillInlineMenuButtonIframe(this);
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -293,18 +296,19 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte
|
||||
if (this.isFirefoxBrowser) {
|
||||
this.listElement = globalThis.document.createElement("div");
|
||||
this.listElement.setAttribute("popover", "manual");
|
||||
new AutofillInlineMenuListIframe(this.listElement);
|
||||
this.listIframe = new AutofillInlineMenuListIframe(this.listElement);
|
||||
|
||||
return this.listElement;
|
||||
}
|
||||
|
||||
const customElementName = this.generateRandomCustomElementName();
|
||||
const self = this;
|
||||
globalThis.customElements?.define(
|
||||
customElementName,
|
||||
class extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
new AutofillInlineMenuListIframe(this);
|
||||
self.listIframe = new AutofillInlineMenuListIframe(this);
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -778,5 +782,13 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte
|
||||
this.closeInlineMenu();
|
||||
this.clearPersistentLastChildOverrideTimeout();
|
||||
this.unobservePageAttributes();
|
||||
this.unobserveCustomElements();
|
||||
this.unobserveContainerElement();
|
||||
if (this.mutationObserverIterationsResetTimeout) {
|
||||
clearTimeout(this.mutationObserverIterationsResetTimeout);
|
||||
this.mutationObserverIterationsResetTimeout = null;
|
||||
}
|
||||
this.buttonIframe?.destroy();
|
||||
this.listIframe?.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { AutofillInlineMenuIframeService } from "./autofill-inline-menu-iframe.service";
|
||||
|
||||
export class AutofillInlineMenuIframeElement {
|
||||
private autofillInlineMenuIframeService: AutofillInlineMenuIframeService;
|
||||
|
||||
constructor(
|
||||
element: HTMLElement,
|
||||
portName: string,
|
||||
@@ -12,14 +14,14 @@ export class AutofillInlineMenuIframeElement {
|
||||
const shadow: ShadowRoot = element.attachShadow({ mode: "closed" });
|
||||
shadow.prepend(style);
|
||||
|
||||
const autofillInlineMenuIframeService = new AutofillInlineMenuIframeService(
|
||||
this.autofillInlineMenuIframeService = new AutofillInlineMenuIframeService(
|
||||
shadow,
|
||||
portName,
|
||||
initStyles,
|
||||
iframeTitle,
|
||||
ariaAlert,
|
||||
);
|
||||
autofillInlineMenuIframeService.initMenuIframe();
|
||||
this.autofillInlineMenuIframeService.initMenuIframe();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,4 +69,11 @@ export class AutofillInlineMenuIframeElement {
|
||||
|
||||
return style;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the iframe service to prevent memory leaks.
|
||||
*/
|
||||
destroy() {
|
||||
this.autofillInlineMenuIframeService?.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -555,4 +555,26 @@ export class AutofillInlineMenuIframeService implements AutofillInlineMenuIframe
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up all event listeners, timeouts, and observers to prevent memory leaks.
|
||||
*/
|
||||
destroy() {
|
||||
this.iframe?.removeEventListener(EVENTS.LOAD, this.setupPortMessageListener);
|
||||
this.clearAriaAlert();
|
||||
this.clearFadeInTimeout();
|
||||
if (this.delayedCloseTimeout) {
|
||||
clearTimeout(this.delayedCloseTimeout);
|
||||
this.delayedCloseTimeout = null;
|
||||
}
|
||||
if (this.mutationObserverIterationsResetTimeout) {
|
||||
clearTimeout(this.mutationObserverIterationsResetTimeout);
|
||||
this.mutationObserverIterationsResetTimeout = null;
|
||||
}
|
||||
this.unobserveIframe();
|
||||
this.port?.onMessage.removeListener(this.handlePortMessage);
|
||||
this.port?.onDisconnect.removeListener(this.handlePortDisconnect);
|
||||
this.port?.disconnect();
|
||||
this.port = null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user