From dfc18d6d0ecb64064d7a028571298662ae6a7efd Mon Sep 17 00:00:00 2001 From: nick-delirium Date: Mon, 2 Sep 2024 15:14:03 +0200 Subject: [PATCH] fix tracker: testing memory clearing on iframe src change fix tracker: testing memory clearing on iframe src change --- .../tracker/src/main/app/observer/observer.ts | 56 ++++++++++++++++--- 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/tracker/tracker/src/main/app/observer/observer.ts b/tracker/tracker/src/main/app/observer/observer.ts index adbada4e9..65d1bcd2b 100644 --- a/tracker/tracker/src/main/app/observer/observer.ts +++ b/tracker/tracker/src/main/app/observer/observer.ts @@ -1,4 +1,4 @@ -import { createMutationObserver, ngSafeBrowserMethod } from '../../utils.js' +import { createMutationObserver } from '../../utils.js' import { RemoveNodeAttribute, SetNodeAttributeURLBased, @@ -77,13 +77,11 @@ export default abstract class Observer { // mutations order is sequential const target = mutation.target const type = mutation.type - if (!isObservable(target)) { continue } if (type === 'childList') { for (let i = 0; i < mutation.removedNodes.length; i++) { - // Should be the same as bindTree(mutation.removedNodes[i]), but logic needs to be be untied if (isObservable(mutation.removedNodes[i])) { this.bindNode(mutation.removedNodes[i]) } @@ -105,6 +103,9 @@ export default abstract class Observer { if (name === null) { continue } + if (target instanceof HTMLIFrameElement && name === 'src') { + this.handleIframeSrcChange(target) + } let attr = this.attributesMap.get(id) if (attr === undefined) { this.attributesMap.set(id, (attr = new Set())) @@ -114,13 +115,13 @@ export default abstract class Observer { } if (type === 'characterData') { this.textSet.add(id) - continue } } this.commitNodes() }) as MutationCallback, ) } + private clear(): void { this.commited.length = 0 this.recents.clear() @@ -129,10 +130,49 @@ export default abstract class Observer { this.textSet.clear() } + /** + * Unbinds the removed nodes in case of iframe src change. + */ + private handleIframeSrcChange(iframe: HTMLIFrameElement): void { + const oldContentDocument = iframe.contentDocument + if (oldContentDocument) { + const id = this.app.nodes.getID(oldContentDocument) + if (id !== undefined) { + const walker = document.createTreeWalker( + oldContentDocument, + NodeFilter.SHOW_ELEMENT + NodeFilter.SHOW_TEXT, + { + acceptNode: (node) => + isIgnored(node) || this.app.nodes.getID(node) === undefined + ? NodeFilter.FILTER_REJECT + : NodeFilter.FILTER_ACCEPT, + }, + // @ts-ignore + false, + ) + + let removed = 0 + const totalBeforeRemove = this.app.nodes.getNodeCount() + + while (walker.nextNode()) { + if (!iframe.contentDocument.contains(walker.currentNode)) { + removed += 1 + this.app.nodes.unregisterNode(walker.currentNode) + } + } + + const removedPercent = Math.floor((removed / totalBeforeRemove) * 100) + if (removedPercent > 30) { + this.app.send(UnbindNodes(removedPercent)) + } + } + } + } + private sendNodeAttribute(id: number, node: Element, name: string, value: string | null): void { if (isSVGElement(node)) { - if (name.substr(0, 6) === 'xlink:') { - name = name.substr(6) + if (name.substring(0, 6) === 'xlink:') { + name = name.substring(6) } if (value === null) { this.app.send(RemoveNodeAttribute(id, name)) @@ -152,7 +192,7 @@ export default abstract class Observer { name === 'integrity' || name === 'crossorigin' || name === 'autocomplete' || - name.substr(0, 2) === 'on' + name.substring(0, 2) === 'on' ) { return } @@ -357,6 +397,7 @@ export default abstract class Observer { } return true } + private commitNode(id: number): boolean { const node = this.app.nodes.getNode(id) if (node === undefined) { @@ -368,6 +409,7 @@ export default abstract class Observer { } return (this.commited[id] = this._commitNode(id, node)) } + private commitNodes(isStart = false): void { let node this.recents.forEach((type, id) => {