microsoft / microsoft/vscode

DOM: shared mutation observer is never disconnected once two consumers share it

Open
#332,616 0 comments 0 reactions 1 assignee Claimed by @benibenj View on GitHub
Dominant language
TypeScript
Stars
193k
Forks
42.4k
PR merge metrics
PR metrics pending

Description

### Bug description

`sharedMutationObserver.observe` (`src/vs/base/browser/dom.ts`) shares one `MutationObserver` per target and options hash and counts `users`. The creating consumer registers a cleanup disposable that decrements the count and disconnects the observer when it reaches zero. A second consumer that joins an existing observer only increments `users` but registers no cleanup of its own, so once two consumers share an observer the count can never reach zero again: the first consumer's decrement takes it from 2 to 1, where it stays forever.

Result: the shared `MutationObserver` is never disconnected, its `onDidMutate` emitter is never disposed, and both the target node and the map entries stay pinned for the rest of the session.

Reachable in practice through style sharing across windows: cloning a global stylesheet makes a second store observe the same node.

### Steps to reproduce

1. Consumer A calls `sharedMutationObserver.observe(node, options)` and disposes the returned subscription after consumer B has joined.
2. Observe that `users` drops to 1 but never 0: `observer.disconnect()` never runs even after B also disposes.

### Expected behavior

Every observing consumer holds its own cleanup disposable; when the last one disposes, count reaches 0, the emitter is disposed and the observer disconnected exactly once regardless of dispose order.

### Version tested

Commit `38ec3d57f91b` on `main`; reproduced with deterministic DOM unit tests (joiner-first and creator-first dispose order). A fix is ready.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.