"Detached Elements" does not show nodes that were never attached
Hi, Thanks for a great tool!
I discovered one problem with it that I think should be treated as a bug. Or, probably the docs need a clarification.
The announcement blog post (https://blogs.windows.com/msedgedev/2021/12/09/debug-memory-leaks-detached-elements-tool-devtools/) gives 2 valid reasons why elements can be unattached (numbering mine):
Usually, we create DOM nodes in JavaScript to insert them somewhere in the page to display them. But it is possible to create nodes and (1) never attach them or (2) remove nodes from the page and keep references to them in JavaScript.
The announcement blog post explains it as if the "Detached Elements" tab was able to detect cases (1) and (2), but, in fact, it only detects case (2).
I think both cases are valid. It would be useful for this tool to support both. Ideally with some button to filter the kind you're interested in.
Reproduction
Run the following code as index.html:
<script type="module">
const cache = [];
const detachedSection = document.createElement("section");
cache.push(detachedSection);
const detachedArticle = document.createElement("article");
document.body.appendChild(detachedArticle);
detachedArticle.remove();
cache.push(detachedArticle);
console.log("Disconnected elements", cache);
</script>
This script creates two nodes, but only one (article) is reported by the tool, as seen on the screenshot below:
Edge version: 120.0.2158.0 (Official build) Canary (arm64)
Thanks for reaching out and reporting this. Let me take this with the dev team and get back to you with more details.
A homebrew solution, that worked in my case, is to monkey-patch document.createElement() and document.createTextNode() to attach the created nodes to a temporary element, and immediately detach them.
const tmpAttachment = document.createElement('div');
tmpAttachment.id = 'tmpAttachment';
document.body.appendChild(tmpAttachment);
{
const old = document.createElement;
document.createElement = function (...args) {
const element = old.apply(document, args);
tmpAttachment.appendChild(element);
tmpAttachment.replaceChildren();
return element;
};
}
{
const old = document.createTextNode;
document.createTextNode = function (...args) {
const element = old.apply(document, args);
tmpAttachment.appendChild(element);
tmpAttachment.replaceChildren();
return element;
};
}
Cool! Thanks for sharing. Note that we're still looking into this.
I took a quick look at the implementation and my conclusion is that if it's not by design, it's at least a limitation of the current implementation. I haven't worked on the code base directly for a long time, and my knowledge of the C++ code of Chromium is limited, but my understanding is that the detached elements are retrieved by a process that gets kicked off when a node is removed from the DOM.
In Chromium, here, the function DispatchChildRemovalEvents starts with probe::WillRemoveDOMNode(&child); which is what, I think, starts off the whole process.
@joselea I know you don't work on this right now, but by any chance, would you remember something about this?
This is an interesting edge case. The tool was never designed to handle this specific case (for full disclosure, and this might clarify why the tool behaves the way it does, it monitors the removal of nodes from the DOM and keeps those in a weak-ref list; when the API is called, they're re-checked).
That having been said, we're currently working on upstreaming similar functionality to the overall Chromium project; and the way in which this new functionality is built (using the V8 heap walker) is likely to be able to detect this case. I can't make promises about when exactly this will land but I'm estimating that it'll probably be around Chromium/Edge 129.