Change:
<!-- Do Not Delete This! bugreporttemplate --<!-- Please read our Rules of Conduct: --<!-- Read our guide about submitting issues: --<!-- Search existing issues to avoid creating duplicates. --<!-- Test using the latest Insiders build to see if your issue has already been fixed: --<!-- Instead of creating your report here, use 'Report Issue' from the 'Help' menu in VS Code to pre-fill useful information. --<!-- Launch with to check. --Does this issue occur when all extensions are disabled?: Yes <!-- If you answered No above, use 'Help: Start Extension Bisect' from Command Palette to try to identify the cause. --<!-- Issues caused by an extension need to be reported directly to the extension publisher. The 'Help Report Issue' dialog can assist with this. --- VS Code Version: 1.83.0 OS Version: Ubuntu 23.04 Steps to Reproduce: a VSCode Window the menu at the top, click on "File" which opens the menu the menu at the top, click on "File" again which closes the menu keydown event listener has been , but not been removed It seems the issue is here:
Thanks for creating this issue! It looks like you may be using an old version of VS Code, the latest stable release is 1.83.1. Please try upgrading to the latest version and checking whether this issue remains. Happy Coding!
Thanks ! I'm wondering how you got the event listener list?
You're welcome! I have a project here: There are several end-to-end tests, which also measure event listeners to find memory leaks. The end-to-end test is defined here: It can be executed like this: Before and after a test is executed, all event listeners are queried using Chrome Devtools Protocol and . We get an array of event listeners and , for example and The and arrays are compared to see which event listeners have been . In the example above, there is one keydown listener more in the array which is not in the array. The tests are structured in a way one would be expect that the number of event listeners before and after the test are equal. For example, when opening and closing the menu, one would expect the number of event listeners stays equal. This is the menu toggle test: Every time the test was executed, event listeners increased by one keydown listener in , which indicates a memory leak and in this case is precisely the location of the memory leak. In other cases, the output for memory leaks might not be quite as clear, but maybe still helpful. This is the output for the notebook-open test (opening and closing a notebook): It seems there is memory leak when opening and closing a notebook. But just looking at the output, I can't say much more. I'm not even sure where exactly the memory leak is and would need to look more closely at the and code. I hope this helps! :)
This is really cool, feel free to file more issues as you find them! How do you run VS Code in this model, are you starting it via playwright? Is it the web version or the electron version?
How it runs: default the VSCode electron application is downloaded using or if it exists the environment variable is used is launched using electron application in debug mode always prints a websocket url to stderr, for example connections are created using the Websocket connections, chrome devtools protocol commands are being sent and events received a VSCode window is launched, a script is injected into the page which makes the testing easier, e.g. finds the active input element, updates the input and triggers and events. tests run in nodejs, but make use of the injected test code by sending devtools protocol websocket messages, for example <brThe tests were initially using playwright, though eventually it seemed useful to have more customization, for example reusing the same VSCode instance for all tests so that the tests execute faster. All in all, it's very similar to the way playwright runs end-to-end tests. :)

Discussion:
export class Menu extends ActionBar { private mnemonics: Map<string, Array<BaseMenuActionViewItem>>; <del> private readonly menuDisposables: DisposableStore; </del> private scrollableElement: DomScrollableElement; private menuElement: HTMLElement; static globalStyleSheet: HTMLStyleElement;

this.actionsList.tabIndex = 0; <del> this.menuDisposables = this._register(new DisposableStore()); </del> this.initializeOrUpdateStyleSheet(container, menuStyles); this._register(Gesture.addTarget(menuElement)); <del> addDisposableListener(menuElement, EventType.KEY_DOWN, (e) => { </del> <ins> this._register(addDisposableListener(menuElement, EventType.KEY_DOWN, (e) => { </ins> const event = new StandardKeyboardEvent(e); // Stop tab navigation of menus if (event.equals(KeyCode.Tab)) { e.preventDefault(); } <del> }); </del> <ins> })); </ins> if (options.enableMnemonics) { <del> this.menuDisposables.add(addDisposableListener(menuElement, EventType.KEY_DOWN, (e) => { </del> <ins> this._register(addDisposableListener(menuElement, EventType.KEY_DOWN, (e) => { </ins> const key = e.key.toLocaleLowerCase(); if (this.mnemonics.has(key)) { EventHelper.stop(e, true);